nokogiri 1.12.5 → 1.14.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +41 -0
  3. data/LICENSE-DEPENDENCIES.md +830 -509
  4. data/LICENSE.md +1 -1
  5. data/README.md +23 -14
  6. data/bin/nokogiri +63 -50
  7. data/dependencies.yml +33 -66
  8. data/ext/nokogiri/extconf.rb +159 -63
  9. data/ext/nokogiri/gumbo.c +21 -11
  10. data/ext/nokogiri/html4_document.c +2 -2
  11. data/ext/nokogiri/html4_element_description.c +1 -1
  12. data/ext/nokogiri/html4_entity_lookup.c +2 -2
  13. data/ext/nokogiri/html4_sax_parser_context.c +3 -9
  14. data/ext/nokogiri/html4_sax_push_parser.c +1 -1
  15. data/ext/nokogiri/nokogiri.c +38 -51
  16. data/ext/nokogiri/nokogiri.h +26 -14
  17. data/ext/nokogiri/test_global_handlers.c +1 -1
  18. data/ext/nokogiri/xml_attr.c +3 -3
  19. data/ext/nokogiri/xml_attribute_decl.c +5 -5
  20. data/ext/nokogiri/xml_cdata.c +3 -3
  21. data/ext/nokogiri/xml_comment.c +1 -1
  22. data/ext/nokogiri/xml_document.c +53 -44
  23. data/ext/nokogiri/xml_document_fragment.c +1 -3
  24. data/ext/nokogiri/xml_dtd.c +11 -11
  25. data/ext/nokogiri/xml_element_content.c +3 -3
  26. data/ext/nokogiri/xml_element_decl.c +5 -5
  27. data/ext/nokogiri/xml_encoding_handler.c +28 -14
  28. data/ext/nokogiri/xml_entity_decl.c +6 -6
  29. data/ext/nokogiri/xml_entity_reference.c +1 -1
  30. data/ext/nokogiri/xml_namespace.c +80 -14
  31. data/ext/nokogiri/xml_node.c +982 -396
  32. data/ext/nokogiri/xml_node_set.c +4 -6
  33. data/ext/nokogiri/xml_processing_instruction.c +1 -1
  34. data/ext/nokogiri/xml_reader.c +133 -32
  35. data/ext/nokogiri/xml_relax_ng.c +1 -3
  36. data/ext/nokogiri/xml_sax_parser.c +23 -17
  37. data/ext/nokogiri/xml_sax_parser_context.c +11 -9
  38. data/ext/nokogiri/xml_sax_push_parser.c +1 -3
  39. data/ext/nokogiri/xml_schema.c +4 -6
  40. data/ext/nokogiri/xml_syntax_error.c +1 -1
  41. data/ext/nokogiri/xml_text.c +2 -2
  42. data/ext/nokogiri/xml_xpath_context.c +144 -114
  43. data/ext/nokogiri/xslt_stylesheet.c +122 -23
  44. data/gumbo-parser/Makefile +10 -0
  45. data/gumbo-parser/src/attribute.h +1 -1
  46. data/gumbo-parser/src/error.c +2 -2
  47. data/gumbo-parser/src/error.h +1 -1
  48. data/gumbo-parser/src/foreign_attrs.c +2 -2
  49. data/gumbo-parser/src/{gumbo.h → nokogiri_gumbo.h} +1 -0
  50. data/gumbo-parser/src/parser.c +8 -16
  51. data/gumbo-parser/src/replacement.h +1 -1
  52. data/gumbo-parser/src/string_buffer.h +1 -1
  53. data/gumbo-parser/src/string_piece.c +1 -1
  54. data/gumbo-parser/src/svg_attrs.c +2 -2
  55. data/gumbo-parser/src/svg_tags.c +2 -2
  56. data/gumbo-parser/src/tag.c +2 -1
  57. data/gumbo-parser/src/tag_lookup.c +7 -7
  58. data/gumbo-parser/src/tag_lookup.gperf +1 -0
  59. data/gumbo-parser/src/tag_lookup.h +1 -1
  60. data/gumbo-parser/src/token_buffer.h +1 -1
  61. data/gumbo-parser/src/tokenizer.c +1 -1
  62. data/gumbo-parser/src/tokenizer.h +1 -1
  63. data/gumbo-parser/src/utf8.c +1 -1
  64. data/gumbo-parser/src/utf8.h +1 -1
  65. data/gumbo-parser/src/util.c +1 -3
  66. data/gumbo-parser/src/util.h +4 -0
  67. data/gumbo-parser/src/vector.h +1 -1
  68. data/lib/nokogiri/class_resolver.rb +67 -0
  69. data/lib/nokogiri/css/node.rb +9 -8
  70. data/lib/nokogiri/css/parser.rb +360 -341
  71. data/lib/nokogiri/css/parser.y +249 -244
  72. data/lib/nokogiri/css/parser_extras.rb +22 -20
  73. data/lib/nokogiri/css/syntax_error.rb +1 -0
  74. data/lib/nokogiri/css/tokenizer.rb +4 -3
  75. data/lib/nokogiri/css/tokenizer.rex +3 -2
  76. data/lib/nokogiri/css/xpath_visitor.rb +184 -85
  77. data/lib/nokogiri/css.rb +44 -6
  78. data/lib/nokogiri/decorators/slop.rb +8 -7
  79. data/lib/nokogiri/encoding_handler.rb +57 -0
  80. data/lib/nokogiri/extension.rb +4 -3
  81. data/lib/nokogiri/gumbo.rb +1 -0
  82. data/lib/nokogiri/html.rb +16 -10
  83. data/lib/nokogiri/html4/builder.rb +1 -0
  84. data/lib/nokogiri/html4/document.rb +56 -164
  85. data/lib/nokogiri/html4/document_fragment.rb +11 -7
  86. data/lib/nokogiri/html4/element_description.rb +1 -0
  87. data/lib/nokogiri/html4/element_description_defaults.rb +432 -532
  88. data/lib/nokogiri/html4/encoding_reader.rb +121 -0
  89. data/lib/nokogiri/html4/entity_lookup.rb +2 -1
  90. data/lib/nokogiri/html4/sax/parser.rb +5 -2
  91. data/lib/nokogiri/html4/sax/parser_context.rb +1 -0
  92. data/lib/nokogiri/html4/sax/push_parser.rb +7 -7
  93. data/lib/nokogiri/html4.rb +12 -5
  94. data/lib/nokogiri/html5/document.rb +126 -32
  95. data/lib/nokogiri/html5/document_fragment.rb +14 -4
  96. data/lib/nokogiri/html5/node.rb +12 -7
  97. data/lib/nokogiri/html5.rb +138 -222
  98. data/lib/nokogiri/jruby/dependencies.rb +2 -19
  99. data/lib/nokogiri/jruby/nokogiri_jars.rb +43 -0
  100. data/lib/nokogiri/syntax_error.rb +1 -0
  101. data/lib/nokogiri/version/constant.rb +2 -1
  102. data/lib/nokogiri/version/info.rb +32 -24
  103. data/lib/nokogiri/version.rb +1 -0
  104. data/lib/nokogiri/xml/attr.rb +54 -3
  105. data/lib/nokogiri/xml/attribute_decl.rb +2 -1
  106. data/lib/nokogiri/xml/builder.rb +35 -33
  107. data/lib/nokogiri/xml/cdata.rb +2 -1
  108. data/lib/nokogiri/xml/character_data.rb +1 -0
  109. data/lib/nokogiri/xml/document.rb +232 -143
  110. data/lib/nokogiri/xml/document_fragment.rb +88 -42
  111. data/lib/nokogiri/xml/dtd.rb +3 -2
  112. data/lib/nokogiri/xml/element_content.rb +1 -0
  113. data/lib/nokogiri/xml/element_decl.rb +2 -1
  114. data/lib/nokogiri/xml/entity_decl.rb +3 -2
  115. data/lib/nokogiri/xml/entity_reference.rb +1 -0
  116. data/lib/nokogiri/xml/namespace.rb +44 -0
  117. data/lib/nokogiri/xml/node/save_options.rb +14 -8
  118. data/lib/nokogiri/xml/node.rb +708 -383
  119. data/lib/nokogiri/xml/node_set.rb +134 -59
  120. data/lib/nokogiri/xml/notation.rb +12 -0
  121. data/lib/nokogiri/xml/parse_options.rb +140 -56
  122. data/lib/nokogiri/xml/pp/character_data.rb +8 -6
  123. data/lib/nokogiri/xml/pp/node.rb +26 -26
  124. data/lib/nokogiri/xml/pp.rb +1 -0
  125. data/lib/nokogiri/xml/processing_instruction.rb +3 -1
  126. data/lib/nokogiri/xml/reader.rb +20 -24
  127. data/lib/nokogiri/xml/relax_ng.rb +1 -0
  128. data/lib/nokogiri/xml/sax/document.rb +20 -19
  129. data/lib/nokogiri/xml/sax/parser.rb +38 -36
  130. data/lib/nokogiri/xml/sax/parser_context.rb +7 -3
  131. data/lib/nokogiri/xml/sax/push_parser.rb +5 -5
  132. data/lib/nokogiri/xml/sax.rb +1 -0
  133. data/lib/nokogiri/xml/schema.rb +7 -6
  134. data/lib/nokogiri/xml/searchable.rb +93 -62
  135. data/lib/nokogiri/xml/syntax_error.rb +5 -4
  136. data/lib/nokogiri/xml/text.rb +1 -0
  137. data/lib/nokogiri/xml/xpath/syntax_error.rb +2 -1
  138. data/lib/nokogiri/xml/xpath.rb +12 -0
  139. data/lib/nokogiri/xml/xpath_context.rb +2 -3
  140. data/lib/nokogiri/xml.rb +4 -3
  141. data/lib/nokogiri/xslt/stylesheet.rb +1 -0
  142. data/lib/nokogiri/xslt.rb +21 -13
  143. data/lib/nokogiri.rb +22 -27
  144. data/lib/xsd/xmlparser/nokogiri.rb +28 -25
  145. data/patches/libxml2/0009-allow-wildcard-namespaces.patch +77 -0
  146. data/patches/libxslt/0001-update-automake-files-for-arm64.patch +2445 -1919
  147. data/ports/archives/libxml2-2.10.4.tar.xz +0 -0
  148. data/ports/archives/libxslt-1.1.37.tar.xz +0 -0
  149. metadata +20 -171
  150. data/patches/libxml2/0004-use-glibc-strlen.patch +0 -53
  151. data/patches/libxml2/0005-avoid-isnan-isinf.patch +0 -81
  152. data/patches/libxml2/0006-update-automake-files-for-arm64.patch +0 -2511
  153. data/patches/libxml2/0007-Fix-XPath-recursion-limit.patch +0 -31
  154. data/patches/libxslt/0002-Fix-xml2-config-check-in-configure-script.patch +0 -19
  155. data/ports/archives/libxml2-2.9.12.tar.gz +0 -0
  156. data/ports/archives/libxslt-1.1.34.tar.gz +0 -0
@@ -1,23 +1,23 @@
1
1
  #include <nokogiri.h>
2
2
 
3
- VALUE cNokogiriXmlNode ;
3
+ #include <stdbool.h>
4
+
5
+ // :stopdoc:
4
6
 
7
+ VALUE cNokogiriXmlNode ;
5
8
  static ID id_decorate, id_decorate_bang;
6
9
 
7
- #ifdef DEBUG
8
- static void
9
- debug_node_dealloc(xmlNodePtr x)
10
- {
11
- NOKOGIRI_DEBUG_START(x)
12
- NOKOGIRI_DEBUG_END(x)
13
- }
14
- #else
15
- # define debug_node_dealloc 0
16
- #endif
10
+ typedef xmlNodePtr(*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
17
11
 
18
12
  static void
19
- mark(xmlNodePtr node)
13
+ _xml_node_mark(void *ptr)
20
14
  {
15
+ xmlNodePtr node = ptr;
16
+
17
+ if (!DOC_RUBY_OBJECT_TEST(node->doc)) {
18
+ return;
19
+ }
20
+
21
21
  xmlDocPtr doc = node->doc;
22
22
  if (doc->type == XML_DOCUMENT_NODE || doc->type == XML_HTML_DOCUMENT_NODE) {
23
23
  if (DOC_RUBY_OBJECT_TEST(doc)) {
@@ -28,10 +28,29 @@ mark(xmlNodePtr node)
28
28
  }
29
29
  }
30
30
 
31
- /* :nodoc: */
32
- typedef xmlNodePtr(*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
31
+ #ifdef HAVE_RB_GC_LOCATION
32
+ static void
33
+ _xml_node_update_references(void *ptr)
34
+ {
35
+ xmlNodePtr node = ptr;
36
+
37
+ if (node->_private) {
38
+ node->_private = (void *)rb_gc_location((VALUE)node->_private);
39
+ }
40
+ }
41
+ #else
42
+ # define _xml_node_update_references 0
43
+ #endif
44
+
45
+ static const rb_data_type_t nokogiri_node_type = {
46
+ "Nokogiri/XMLNode",
47
+ {_xml_node_mark, 0, 0, _xml_node_update_references},
48
+ 0, 0,
49
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
50
+ RUBY_TYPED_FREE_IMMEDIATELY,
51
+ #endif
52
+ };
33
53
 
34
- /* :nodoc: */
35
54
  static void
36
55
  relink_namespace(xmlNodePtr reparented)
37
56
  {
@@ -143,7 +162,9 @@ relink_namespace(xmlNodePtr reparented)
143
162
  }
144
163
  }
145
164
 
146
- /* :nodoc: */
165
+
166
+ /* internal function meant to wrap xmlReplaceNode
167
+ and fix some issues we have with libxml2 merging nodes */
147
168
  static xmlNodePtr
148
169
  xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
149
170
  {
@@ -168,7 +189,18 @@ xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
168
189
  return retval ;
169
190
  }
170
191
 
171
- /* :nodoc: */
192
+
193
+ static void
194
+ raise_if_ancestor_of_self(xmlNodePtr self)
195
+ {
196
+ for (xmlNodePtr ancestor = self->parent ; ancestor ; ancestor = ancestor->parent) {
197
+ if (self == ancestor) {
198
+ rb_raise(rb_eRuntimeError, "cycle detected: node '%s' is an ancestor of itself", self->name);
199
+ }
200
+ }
201
+ }
202
+
203
+
172
204
  static VALUE
173
205
  reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
174
206
  {
@@ -183,8 +215,8 @@ reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func
183
215
  rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
184
216
  }
185
217
 
186
- Data_Get_Struct(reparentee_obj, xmlNode, reparentee);
187
- Data_Get_Struct(pivot_obj, xmlNode, pivot);
218
+ Noko_Node_Get_Struct(reparentee_obj, xmlNode, reparentee);
219
+ Noko_Node_Get_Struct(pivot_obj, xmlNode, pivot);
188
220
 
189
221
  /*
190
222
  * Check if nodes given are appropriate to have a parent-child
@@ -357,49 +389,421 @@ ok:
357
389
  * adjacent text nodes.
358
390
  */
359
391
  DATA_PTR(reparentee_obj) = reparented ;
360
-
361
- relink_namespace(reparented);
362
-
363
392
  reparented_obj = noko_xml_node_wrap(Qnil, reparented);
364
393
 
365
394
  rb_funcall(reparented_obj, id_decorate_bang, 0);
366
395
 
396
+ /* if we've created a cycle, raise an exception */
397
+ raise_if_ancestor_of_self(reparented);
398
+
399
+ relink_namespace(reparented);
400
+
367
401
  return reparented_obj ;
368
402
  }
369
403
 
404
+ // :startdoc:
370
405
 
371
406
  /*
372
- * call-seq:
373
- * document
407
+ * :call-seq:
408
+ * add_namespace_definition(prefix, href) → Nokogiri::XML::Namespace
409
+ * add_namespace(prefix, href) → Nokogiri::XML::Namespace
410
+ *
411
+ * :category: Manipulating Document Structure
412
+ *
413
+ * Adds a namespace definition to this node with +prefix+ using +href+ value, as if this node had
414
+ * included an attribute "xmlns:prefix=href".
415
+ *
416
+ * A default namespace definition for this node can be added by passing +nil+ for +prefix+.
417
+ *
418
+ * [Parameters]
419
+ * - +prefix+ (String, +nil+) An {XML Name}[https://www.w3.org/TR/xml-names/#ns-decl]
420
+ * - +href+ (String) The {URI reference}[https://www.w3.org/TR/xml-names/#sec-namespaces]
421
+ *
422
+ * [Returns] The new Nokogiri::XML::Namespace
423
+ *
424
+ * *Example:* adding a non-default namespace definition
425
+ *
426
+ * doc = Nokogiri::XML("<store><inventory></inventory></store>")
427
+ * inventory = doc.at_css("inventory")
428
+ * inventory.add_namespace_definition("automobile", "http://alices-autos.com/")
429
+ * inventory.add_namespace_definition("bicycle", "http://bobs-bikes.com/")
430
+ * inventory.add_child("<automobile:tire>Michelin model XGV, size 75R</automobile:tire>")
431
+ * doc.to_xml
432
+ * # => "<?xml version=\"1.0\"?>\n" +
433
+ * # "<store>\n" +
434
+ * # " <inventory xmlns:automobile=\"http://alices-autos.com/\" xmlns:bicycle=\"http://bobs-bikes.com/\">\n" +
435
+ * # " <automobile:tire>Michelin model XGV, size 75R</automobile:tire>\n" +
436
+ * # " </inventory>\n" +
437
+ * # "</store>\n"
438
+ *
439
+ * *Example:* adding a default namespace definition
440
+ *
441
+ * doc = Nokogiri::XML("<store><inventory><tire>Michelin model XGV, size 75R</tire></inventory></store>")
442
+ * doc.at_css("tire").add_namespace_definition(nil, "http://bobs-bikes.com/")
443
+ * doc.to_xml
444
+ * # => "<?xml version=\"1.0\"?>\n" +
445
+ * # "<store>\n" +
446
+ * # " <inventory>\n" +
447
+ * # " <tire xmlns=\"http://bobs-bikes.com/\">Michelin model XGV, size 75R</tire>\n" +
448
+ * # " </inventory>\n" +
449
+ * # "</store>\n"
374
450
  *
375
- * Get the document for this Node
376
451
  */
377
452
  static VALUE
378
- document(VALUE self)
453
+ rb_xml_node_add_namespace_definition(VALUE rb_node, VALUE rb_prefix, VALUE rb_href)
454
+ {
455
+ xmlNodePtr c_node, element;
456
+ xmlNsPtr c_namespace;
457
+ const xmlChar *c_prefix = (const xmlChar *)(NIL_P(rb_prefix) ? NULL : StringValueCStr(rb_prefix));
458
+
459
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
460
+ element = c_node ;
461
+
462
+ c_namespace = xmlSearchNs(c_node->doc, c_node, c_prefix);
463
+
464
+ if (!c_namespace) {
465
+ if (c_node->type != XML_ELEMENT_NODE) {
466
+ element = c_node->parent;
467
+ }
468
+ c_namespace = xmlNewNs(element, (const xmlChar *)StringValueCStr(rb_href), c_prefix);
469
+ }
470
+
471
+ if (!c_namespace) {
472
+ return Qnil ;
473
+ }
474
+
475
+ if (NIL_P(rb_prefix) || c_node != element) {
476
+ xmlSetNs(c_node, c_namespace);
477
+ }
478
+
479
+ return noko_xml_namespace_wrap(c_namespace, c_node->doc);
480
+ }
481
+
482
+
483
+ /*
484
+ * :call-seq: attribute(name) → Nokogiri::XML::Attr
485
+ *
486
+ * :category: Working With Node Attributes
487
+ *
488
+ * [Returns] Attribute (Nokogiri::XML::Attr) belonging to this node with name +name+.
489
+ *
490
+ * ⚠ Note that attribute namespaces are ignored and only the simple (non-namespace-prefixed) name is
491
+ * used to find a matching attribute. In case of a simple name collision, only one of the matching
492
+ * attributes will be returned. In this case, you will need to use #attribute_with_ns.
493
+ *
494
+ * *Example:*
495
+ *
496
+ * doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
497
+ * child = doc.at_css("child")
498
+ * child.attribute("size") # => #<Nokogiri::XML::Attr:0x550 name="size" value="large">
499
+ * child.attribute("class") # => #<Nokogiri::XML::Attr:0x564 name="class" value="big wide tall">
500
+ *
501
+ * *Example* showing that namespaced attributes will not be returned:
502
+ *
503
+ * ⚠ Note that only one of the two matching attributes is returned.
504
+ *
505
+ * doc = Nokogiri::XML(<<~EOF)
506
+ * <root xmlns:width='http://example.com/widths'
507
+ * xmlns:height='http://example.com/heights'>
508
+ * <child width:size='broad' height:size='tall'/>
509
+ * </root>
510
+ * EOF
511
+ * doc.at_css("child").attribute("size")
512
+ * # => #(Attr:0x550 {
513
+ * # name = "size",
514
+ * # namespace = #(Namespace:0x564 {
515
+ * # prefix = "width",
516
+ * # href = "http://example.com/widths"
517
+ * # }),
518
+ * # value = "broad"
519
+ * # })
520
+ */
521
+ static VALUE
522
+ rb_xml_node_attribute(VALUE self, VALUE name)
523
+ {
524
+ xmlNodePtr node;
525
+ xmlAttrPtr prop;
526
+ Noko_Node_Get_Struct(self, xmlNode, node);
527
+ prop = xmlHasProp(node, (xmlChar *)StringValueCStr(name));
528
+
529
+ if (! prop) { return Qnil; }
530
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
531
+ }
532
+
533
+
534
+ /*
535
+ * :call-seq: attribute_nodes() → Array<Nokogiri::XML::Attr>
536
+ *
537
+ * :category: Working With Node Attributes
538
+ *
539
+ * [Returns] Attributes (an Array of Nokogiri::XML::Attr) belonging to this node.
540
+ *
541
+ * Note that this is the preferred alternative to #attributes when the simple
542
+ * (non-namespace-prefixed) attribute names may collide.
543
+ *
544
+ * *Example:*
545
+ *
546
+ * Contrast this with the colliding-name example from #attributes.
547
+ *
548
+ * doc = Nokogiri::XML(<<~EOF)
549
+ * <root xmlns:width='http://example.com/widths'
550
+ * xmlns:height='http://example.com/heights'>
551
+ * <child width:size='broad' height:size='tall'/>
552
+ * </root>
553
+ * EOF
554
+ * doc.at_css("child").attribute_nodes
555
+ * # => [#(Attr:0x550 {
556
+ * # name = "size",
557
+ * # namespace = #(Namespace:0x564 {
558
+ * # prefix = "width",
559
+ * # href = "http://example.com/widths"
560
+ * # }),
561
+ * # value = "broad"
562
+ * # }),
563
+ * # #(Attr:0x578 {
564
+ * # name = "size",
565
+ * # namespace = #(Namespace:0x58c {
566
+ * # prefix = "height",
567
+ * # href = "http://example.com/heights"
568
+ * # }),
569
+ * # value = "tall"
570
+ * # })]
571
+ */
572
+ static VALUE
573
+ rb_xml_node_attribute_nodes(VALUE rb_node)
574
+ {
575
+ xmlNodePtr c_node;
576
+
577
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
578
+
579
+ return noko_xml_node_attrs(c_node);
580
+ }
581
+
582
+
583
+ /*
584
+ * :call-seq: attribute_with_ns(name, namespace) → Nokogiri::XML::Attr
585
+ *
586
+ * :category: Working With Node Attributes
587
+ *
588
+ * [Returns]
589
+ * Attribute (Nokogiri::XML::Attr) belonging to this node with matching +name+ and +namespace+.
590
+ *
591
+ * [Parameters]
592
+ * - +name+ (String): the simple (non-namespace-prefixed) name of the attribute
593
+ * - +namespace+ (String): the URI of the attribute's namespace
594
+ *
595
+ * See related: #attribute
596
+ *
597
+ * *Example:*
598
+ *
599
+ * doc = Nokogiri::XML(<<~EOF)
600
+ * <root xmlns:width='http://example.com/widths'
601
+ * xmlns:height='http://example.com/heights'>
602
+ * <child width:size='broad' height:size='tall'/>
603
+ * </root>
604
+ * EOF
605
+ * doc.at_css("child").attribute_with_ns("size", "http://example.com/widths")
606
+ * # => #(Attr:0x550 {
607
+ * # name = "size",
608
+ * # namespace = #(Namespace:0x564 {
609
+ * # prefix = "width",
610
+ * # href = "http://example.com/widths"
611
+ * # }),
612
+ * # value = "broad"
613
+ * # })
614
+ * doc.at_css("child").attribute_with_ns("size", "http://example.com/heights")
615
+ * # => #(Attr:0x578 {
616
+ * # name = "size",
617
+ * # namespace = #(Namespace:0x58c {
618
+ * # prefix = "height",
619
+ * # href = "http://example.com/heights"
620
+ * # }),
621
+ * # value = "tall"
622
+ * # })
623
+ */
624
+ static VALUE
625
+ rb_xml_node_attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
379
626
  {
380
627
  xmlNodePtr node;
381
- Data_Get_Struct(self, xmlNode, node);
628
+ xmlAttrPtr prop;
629
+ Noko_Node_Get_Struct(self, xmlNode, node);
630
+ prop = xmlHasNsProp(node, (xmlChar *)StringValueCStr(name),
631
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace));
632
+
633
+ if (! prop) { return Qnil; }
634
+ return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
635
+ }
636
+
637
+
638
+
639
+ /*
640
+ * call-seq: blank? → Boolean
641
+ *
642
+ * [Returns] +true+ if the node is an empty or whitespace-only text or cdata node, else +false+.
643
+ *
644
+ * *Example:*
645
+ *
646
+ * Nokogiri("<root><child/></root>").root.child.blank? # => false
647
+ * Nokogiri("<root>\t \n</root>").root.child.blank? # => true
648
+ * Nokogiri("<root><![CDATA[\t \n]]></root>").root.child.blank? # => true
649
+ * Nokogiri("<root>not-blank</root>").root.child
650
+ * .tap { |n| n.content = "" }.blank # => true
651
+ */
652
+ static VALUE
653
+ rb_xml_node_blank_eh(VALUE self)
654
+ {
655
+ xmlNodePtr node;
656
+ Noko_Node_Get_Struct(self, xmlNode, node);
657
+ return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
658
+ }
659
+
660
+
661
+ /*
662
+ * :call-seq: child() → Nokogiri::XML::Node
663
+ *
664
+ * :category: Traversing Document Structure
665
+ *
666
+ * [Returns] First of this node's children, or +nil+ if there are no children
667
+ *
668
+ * This is a convenience method and is equivalent to:
669
+ *
670
+ * node.children.first
671
+ *
672
+ * See related: #children
673
+ */
674
+ static VALUE
675
+ rb_xml_node_child(VALUE self)
676
+ {
677
+ xmlNodePtr node, child;
678
+ Noko_Node_Get_Struct(self, xmlNode, node);
679
+
680
+ child = node->children;
681
+ if (!child) { return Qnil; }
682
+
683
+ return noko_xml_node_wrap(Qnil, child);
684
+ }
685
+
686
+
687
+ /*
688
+ * :call-seq: children() → Nokogiri::XML::NodeSet
689
+ *
690
+ * :category: Traversing Document Structure
691
+ *
692
+ * [Returns] Nokogiri::XML::NodeSet containing this node's children.
693
+ */
694
+ static VALUE
695
+ rb_xml_node_children(VALUE self)
696
+ {
697
+ xmlNodePtr node;
698
+ xmlNodePtr child;
699
+ xmlNodeSetPtr set;
700
+ VALUE document;
701
+ VALUE node_set;
702
+
703
+ Noko_Node_Get_Struct(self, xmlNode, node);
704
+
705
+ child = node->children;
706
+ set = xmlXPathNodeSetCreate(child);
707
+
708
+ document = DOC_RUBY_OBJECT(node->doc);
709
+
710
+ if (!child) { return noko_xml_node_set_wrap(set, document); }
711
+
712
+ child = child->next;
713
+ while (NULL != child) {
714
+ xmlXPathNodeSetAddUnique(set, child);
715
+ child = child->next;
716
+ }
717
+
718
+ node_set = noko_xml_node_set_wrap(set, document);
719
+
720
+ return node_set;
721
+ }
722
+
723
+
724
+ /*
725
+ * :call-seq:
726
+ * content() → String
727
+ * inner_text() → String
728
+ * text() → String
729
+ * to_str() → String
730
+ *
731
+ * [Returns]
732
+ * Contents of all the text nodes in this node's subtree, concatenated together into a single
733
+ * String.
734
+ *
735
+ * ⚠ Note that entities will _always_ be expanded in the returned String.
736
+ *
737
+ * See related: #inner_html
738
+ *
739
+ * *Example* of how entities are handled:
740
+ *
741
+ * Note that <tt>&lt;</tt> becomes <tt><</tt> in the returned String.
742
+ *
743
+ * doc = Nokogiri::XML.fragment("<child>a &lt; b</child>")
744
+ * doc.at_css("child").content
745
+ * # => "a < b"
746
+ *
747
+ * *Example* of how a subtree is handled:
748
+ *
749
+ * Note that the <tt><span></tt> tags are omitted and only the text node contents are returned,
750
+ * concatenated into a single string.
751
+ *
752
+ * doc = Nokogiri::XML.fragment("<child><span>first</span> <span>second</span></child>")
753
+ * doc.at_css("child").content
754
+ * # => "first second"
755
+ */
756
+ static VALUE
757
+ rb_xml_node_content(VALUE self)
758
+ {
759
+ xmlNodePtr node;
760
+ xmlChar *content;
761
+
762
+ Noko_Node_Get_Struct(self, xmlNode, node);
763
+
764
+ content = xmlNodeGetContent(node);
765
+ if (content) {
766
+ VALUE rval = NOKOGIRI_STR_NEW2(content);
767
+ xmlFree(content);
768
+ return rval;
769
+ }
770
+ return Qnil;
771
+ }
772
+
773
+
774
+ /*
775
+ * :call-seq: document() → Nokogiri::XML::Document
776
+ *
777
+ * :category: Traversing Document Structure
778
+ *
779
+ * [Returns] Parent Nokogiri::XML::Document for this node
780
+ */
781
+ static VALUE
782
+ rb_xml_node_document(VALUE self)
783
+ {
784
+ xmlNodePtr node;
785
+ Noko_Node_Get_Struct(self, xmlNode, node);
382
786
  return DOC_RUBY_OBJECT(node->doc);
383
787
  }
384
788
 
385
789
  /*
386
- * call-seq:
387
- * pointer_id
790
+ * :call-seq: pointer_id() → Integer
388
791
  *
389
- * Get the internal pointer number
792
+ * [Returns]
793
+ * A unique id for this node based on the internal memory structures. This method is used by #==
794
+ * to determine node identity.
390
795
  */
391
796
  static VALUE
392
- pointer_id(VALUE self)
797
+ rb_xml_node_pointer_id(VALUE self)
393
798
  {
394
799
  xmlNodePtr node;
395
- Data_Get_Struct(self, xmlNode, node);
800
+ Noko_Node_Get_Struct(self, xmlNode, node);
396
801
 
397
- return INT2NUM((long)(node));
802
+ return rb_uint2inum((uintptr_t)(node));
398
803
  }
399
804
 
400
805
  /*
401
- * call-seq:
402
- * encode_special_chars(string)
806
+ * :call-seq: encode_special_chars(string) → String
403
807
  *
404
808
  * Encode any special characters in +string+
405
809
  */
@@ -410,7 +814,7 @@ encode_special_chars(VALUE self, VALUE string)
410
814
  xmlChar *encoded;
411
815
  VALUE encoded_str;
412
816
 
413
- Data_Get_Struct(self, xmlNode, node);
817
+ Noko_Node_Get_Struct(self, xmlNode, node);
414
818
  encoded = xmlEncodeSpecialChars(
415
819
  node->doc,
416
820
  (const xmlChar *)StringValueCStr(string)
@@ -423,8 +827,8 @@ encode_special_chars(VALUE self, VALUE string)
423
827
  }
424
828
 
425
829
  /*
426
- * call-seq:
427
- * create_internal_subset(name, external_id, system_id)
830
+ * :call-seq:
831
+ * create_internal_subset(name, external_id, system_id)
428
832
  *
429
833
  * Create the internal subset of a document.
430
834
  *
@@ -441,7 +845,7 @@ create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_i
441
845
  xmlDocPtr doc;
442
846
  xmlDtdPtr dtd;
443
847
 
444
- Data_Get_Struct(self, xmlNode, node);
848
+ Noko_Node_Get_Struct(self, xmlNode, node);
445
849
 
446
850
  doc = node->doc;
447
851
 
@@ -462,8 +866,8 @@ create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_i
462
866
  }
463
867
 
464
868
  /*
465
- * call-seq:
466
- * create_external_subset(name, external_id, system_id)
869
+ * :call-seq:
870
+ * create_external_subset(name, external_id, system_id)
467
871
  *
468
872
  * Create an external subset
469
873
  */
@@ -474,7 +878,7 @@ create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_i
474
878
  xmlDocPtr doc;
475
879
  xmlDtdPtr dtd;
476
880
 
477
- Data_Get_Struct(self, xmlNode, node);
881
+ Noko_Node_Get_Struct(self, xmlNode, node);
478
882
 
479
883
  doc = node->doc;
480
884
 
@@ -495,8 +899,8 @@ create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_i
495
899
  }
496
900
 
497
901
  /*
498
- * call-seq:
499
- * external_subset
902
+ * :call-seq:
903
+ * external_subset()
500
904
  *
501
905
  * Get the external subset
502
906
  */
@@ -507,7 +911,7 @@ external_subset(VALUE self)
507
911
  xmlDocPtr doc;
508
912
  xmlDtdPtr dtd;
509
913
 
510
- Data_Get_Struct(self, xmlNode, node);
914
+ Noko_Node_Get_Struct(self, xmlNode, node);
511
915
 
512
916
  if (!node->doc) { return Qnil; }
513
917
 
@@ -520,8 +924,8 @@ external_subset(VALUE self)
520
924
  }
521
925
 
522
926
  /*
523
- * call-seq:
524
- * internal_subset
927
+ * :call-seq:
928
+ * internal_subset()
525
929
  *
526
930
  * Get the internal subset
527
931
  */
@@ -532,7 +936,7 @@ internal_subset(VALUE self)
532
936
  xmlDocPtr doc;
533
937
  xmlDtdPtr dtd;
534
938
 
535
- Data_Get_Struct(self, xmlNode, node);
939
+ Noko_Node_Get_Struct(self, xmlNode, node);
536
940
 
537
941
  if (!node->doc) { return Qnil; }
538
942
 
@@ -545,16 +949,19 @@ internal_subset(VALUE self)
545
949
  }
546
950
 
547
951
  /*
548
- * call-seq:
549
- * dup
550
- * dup(depth)
551
- * dup(depth, new_parent_doc)
952
+ * :call-seq:
953
+ * dup → Nokogiri::XML::Node
954
+ * dup(depth) → Nokogiri::XML::Node
955
+ * dup(depth, new_parent_doc) → Nokogiri::XML::Node
552
956
  *
553
957
  * Copy this node.
554
- * An optional depth may be passed in. 0 is a shallow copy, 1 (the default) is a deep copy.
555
- * An optional new_parent_doc may also be passed in, which will be the new
556
- * node's parent document. Defaults to the current node's document.
557
- * current document.
958
+ *
959
+ * [Parameters]
960
+ * - +depth+ 0 is a shallow copy, 1 (the default) is a deep copy.
961
+ * - +new_parent_doc+
962
+ * The new node's parent Document. Defaults to the this node's document.
963
+ *
964
+ * [Returns] The new Nokgiri::XML::Node
558
965
  */
559
966
  static VALUE
560
967
  duplicate_node(int argc, VALUE *argv, VALUE self)
@@ -565,7 +972,7 @@ duplicate_node(int argc, VALUE *argv, VALUE self)
565
972
  xmlDocPtr new_parent_doc;
566
973
  xmlNodePtr node, dup;
567
974
 
568
- Data_Get_Struct(self, xmlNode, node);
975
+ Noko_Node_Get_Struct(self, xmlNode, node);
569
976
 
570
977
  n_args = rb_scan_args(argc, argv, "02", &r_level, &r_new_parent_doc);
571
978
 
@@ -589,8 +996,8 @@ duplicate_node(int argc, VALUE *argv, VALUE self)
589
996
  }
590
997
 
591
998
  /*
592
- * call-seq:
593
- * unlink
999
+ * :call-seq:
1000
+ * unlink() → self
594
1001
  *
595
1002
  * Unlink this node from its current context.
596
1003
  */
@@ -598,25 +1005,12 @@ static VALUE
598
1005
  unlink_node(VALUE self)
599
1006
  {
600
1007
  xmlNodePtr node;
601
- Data_Get_Struct(self, xmlNode, node);
1008
+ Noko_Node_Get_Struct(self, xmlNode, node);
602
1009
  xmlUnlinkNode(node);
603
1010
  noko_xml_document_pin_node(node);
604
1011
  return self;
605
1012
  }
606
1013
 
607
- /*
608
- * call-seq:
609
- * blank?
610
- *
611
- * Is this node blank?
612
- */
613
- static VALUE
614
- blank_eh(VALUE self)
615
- {
616
- xmlNodePtr node;
617
- Data_Get_Struct(self, xmlNode, node);
618
- return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
619
- }
620
1014
 
621
1015
  /*
622
1016
  * call-seq:
@@ -628,7 +1022,7 @@ static VALUE
628
1022
  next_sibling(VALUE self)
629
1023
  {
630
1024
  xmlNodePtr node, sibling;
631
- Data_Get_Struct(self, xmlNode, node);
1025
+ Noko_Node_Get_Struct(self, xmlNode, node);
632
1026
 
633
1027
  sibling = node->next;
634
1028
  if (!sibling) { return Qnil; }
@@ -646,7 +1040,7 @@ static VALUE
646
1040
  previous_sibling(VALUE self)
647
1041
  {
648
1042
  xmlNodePtr node, sibling;
649
- Data_Get_Struct(self, xmlNode, node);
1043
+ Noko_Node_Get_Struct(self, xmlNode, node);
650
1044
 
651
1045
  sibling = node->prev;
652
1046
  if (!sibling) { return Qnil; }
@@ -664,7 +1058,7 @@ static VALUE
664
1058
  next_element(VALUE self)
665
1059
  {
666
1060
  xmlNodePtr node, sibling;
667
- Data_Get_Struct(self, xmlNode, node);
1061
+ Noko_Node_Get_Struct(self, xmlNode, node);
668
1062
 
669
1063
  sibling = xmlNextElementSibling(node);
670
1064
  if (!sibling) { return Qnil; }
@@ -682,7 +1076,7 @@ static VALUE
682
1076
  previous_element(VALUE self)
683
1077
  {
684
1078
  xmlNodePtr node, sibling;
685
- Data_Get_Struct(self, xmlNode, node);
1079
+ Noko_Node_Get_Struct(self, xmlNode, node);
686
1080
 
687
1081
  /*
688
1082
  * note that we don't use xmlPreviousElementSibling here because it's buggy pre-2.7.7.
@@ -704,60 +1098,34 @@ replace(VALUE self, VALUE new_node)
704
1098
  VALUE reparent = reparent_node_with(self, new_node, xmlReplaceNodeWrapper);
705
1099
 
706
1100
  xmlNodePtr pivot;
707
- Data_Get_Struct(self, xmlNode, pivot);
1101
+ Noko_Node_Get_Struct(self, xmlNode, pivot);
708
1102
  noko_xml_document_pin_node(pivot);
709
1103
 
710
1104
  return reparent;
711
1105
  }
712
1106
 
713
1107
  /*
714
- * call-seq:
715
- * children
1108
+ * :call-seq:
1109
+ * element_children() → NodeSet
1110
+ * elements() → NodeSet
716
1111
  *
717
- * Get the list of children for this node as a NodeSet
718
- */
719
- static VALUE
720
- children(VALUE self)
721
- {
722
- xmlNodePtr node;
723
- xmlNodePtr child;
724
- xmlNodeSetPtr set;
725
- VALUE document;
726
- VALUE node_set;
727
-
728
- Data_Get_Struct(self, xmlNode, node);
729
-
730
- child = node->children;
731
- set = xmlXPathNodeSetCreate(child);
732
-
733
- document = DOC_RUBY_OBJECT(node->doc);
734
-
735
- if (!child) { return noko_xml_node_set_wrap(set, document); }
736
-
737
- child = child->next;
738
- while (NULL != child) {
739
- xmlXPathNodeSetAddUnique(set, child);
740
- child = child->next;
741
- }
742
-
743
- node_set = noko_xml_node_set_wrap(set, document);
744
-
745
- return node_set;
746
- }
747
-
748
- /*
749
- * call-seq:
750
- * element_children
1112
+ * [Returns]
1113
+ * The node's child elements as a NodeSet. Only children that are elements will be returned, which
1114
+ * notably excludes Text nodes.
751
1115
  *
752
- * Get the list of children for this node as a NodeSet. All nodes will be
753
- * element nodes.
1116
+ * *Example:*
754
1117
  *
755
- * Example:
1118
+ * Note that #children returns the Text node "hello" while #element_children does not.
756
1119
  *
757
- * @doc.root.element_children.all? { |x| x.element? } # => true
1120
+ * div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
1121
+ * div.element_children
1122
+ * # => [#<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
1123
+ * div.children
1124
+ * # => [#<Nokogiri::XML::Text:0x64 "hello">,
1125
+ * # #<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
758
1126
  */
759
1127
  static VALUE
760
- element_children(VALUE self)
1128
+ rb_xml_node_element_children(VALUE self)
761
1129
  {
762
1130
  xmlNodePtr node;
763
1131
  xmlNodePtr child;
@@ -765,7 +1133,7 @@ element_children(VALUE self)
765
1133
  VALUE document;
766
1134
  VALUE node_set;
767
1135
 
768
- Data_Get_Struct(self, xmlNode, node);
1136
+ Noko_Node_Get_Struct(self, xmlNode, node);
769
1137
 
770
1138
  child = xmlFirstElementChild(node);
771
1139
  set = xmlXPathNodeSetCreate(child);
@@ -780,44 +1148,31 @@ element_children(VALUE self)
780
1148
  child = xmlNextElementSibling(child);
781
1149
  }
782
1150
 
783
- node_set = noko_xml_node_set_wrap(set, document);
784
-
785
- return node_set;
786
- }
787
-
788
- /*
789
- * call-seq:
790
- * child
791
- *
792
- * Returns the child node
793
- */
794
- static VALUE
795
- child(VALUE self)
796
- {
797
- xmlNodePtr node, child;
798
- Data_Get_Struct(self, xmlNode, node);
799
-
800
- child = node->children;
801
- if (!child) { return Qnil; }
1151
+ node_set = noko_xml_node_set_wrap(set, document);
802
1152
 
803
- return noko_xml_node_wrap(Qnil, child);
1153
+ return node_set;
804
1154
  }
805
1155
 
806
1156
  /*
807
- * call-seq:
808
- * first_element_child
1157
+ * :call-seq:
1158
+ * first_element_child() → Node
809
1159
  *
810
- * Returns the first child node of this node that is an element.
1160
+ * [Returns] The first child Node that is an element.
811
1161
  *
812
- * Example:
1162
+ * *Example:*
813
1163
  *
814
- * @doc.root.first_element_child.element? # => true
1164
+ * Note that the "hello" child, which is a Text node, is skipped and the <tt><span></tt> element is
1165
+ * returned.
1166
+ *
1167
+ * div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
1168
+ * div.first_element_child
1169
+ * # => #(Element:0x3c { name = "span", children = [ #(Text "world")] })
815
1170
  */
816
1171
  static VALUE
817
- first_element_child(VALUE self)
1172
+ rb_xml_node_first_element_child(VALUE self)
818
1173
  {
819
1174
  xmlNodePtr node, child;
820
- Data_Get_Struct(self, xmlNode, node);
1175
+ Noko_Node_Get_Struct(self, xmlNode, node);
821
1176
 
822
1177
  child = xmlFirstElementChild(node);
823
1178
  if (!child) { return Qnil; }
@@ -826,20 +1181,25 @@ first_element_child(VALUE self)
826
1181
  }
827
1182
 
828
1183
  /*
829
- * call-seq:
830
- * last_element_child
1184
+ * :call-seq:
1185
+ * last_element_child() → Node
831
1186
  *
832
- * Returns the last child node of this node that is an element.
1187
+ * [Returns] The last child Node that is an element.
833
1188
  *
834
- * Example:
1189
+ * *Example:*
835
1190
  *
836
- * @doc.root.last_element_child.element? # => true
1191
+ * Note that the "hello" child, which is a Text node, is skipped and the <tt><span>yes</span></tt>
1192
+ * element is returned.
1193
+ *
1194
+ * div = Nokogiri::HTML5("<div><span>no</span><span>yes</span>skip</div>").at_css("div")
1195
+ * div.last_element_child
1196
+ * # => #(Element:0x3c { name = "span", children = [ #(Text "yes")] })
837
1197
  */
838
1198
  static VALUE
839
- last_element_child(VALUE self)
1199
+ rb_xml_node_last_element_child(VALUE self)
840
1200
  {
841
1201
  xmlNodePtr node, child;
842
- Data_Get_Struct(self, xmlNode, node);
1202
+ Noko_Node_Get_Struct(self, xmlNode, node);
843
1203
 
844
1204
  child = xmlLastElementChild(node);
845
1205
  if (!child) { return Qnil; }
@@ -857,7 +1217,7 @@ static VALUE
857
1217
  key_eh(VALUE self, VALUE attribute)
858
1218
  {
859
1219
  xmlNodePtr node;
860
- Data_Get_Struct(self, xmlNode, node);
1220
+ Noko_Node_Get_Struct(self, xmlNode, node);
861
1221
  if (xmlHasProp(node, (xmlChar *)StringValueCStr(attribute))) {
862
1222
  return Qtrue;
863
1223
  }
@@ -874,7 +1234,7 @@ static VALUE
874
1234
  namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
875
1235
  {
876
1236
  xmlNodePtr node;
877
- Data_Get_Struct(self, xmlNode, node);
1237
+ Noko_Node_Get_Struct(self, xmlNode, node);
878
1238
  if (xmlHasNsProp(node, (xmlChar *)StringValueCStr(attribute),
879
1239
  NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace))) {
880
1240
  return Qtrue;
@@ -893,7 +1253,7 @@ set(VALUE self, VALUE property, VALUE value)
893
1253
  {
894
1254
  xmlNodePtr node, cur;
895
1255
  xmlAttrPtr prop;
896
- Data_Get_Struct(self, xmlNode, node);
1256
+ Noko_Node_Get_Struct(self, xmlNode, node);
897
1257
 
898
1258
  /* If a matching attribute node already exists, then xmlSetProp will destroy
899
1259
  * the existing node's children. However, if Nokogiri has a node object
@@ -938,7 +1298,7 @@ get(VALUE self, VALUE rattribute)
938
1298
 
939
1299
  if (NIL_P(rattribute)) { return Qnil; }
940
1300
 
941
- Data_Get_Struct(self, xmlNode, node);
1301
+ Noko_Node_Get_Struct(self, xmlNode, node);
942
1302
  attribute = xmlCharStrdup(StringValueCStr(rattribute));
943
1303
 
944
1304
  colon = DISCARD_CONST_QUAL_XMLCHAR(xmlStrchr(attribute, (const xmlChar)':'));
@@ -980,10 +1340,10 @@ set_namespace(VALUE self, VALUE namespace)
980
1340
  xmlNodePtr node;
981
1341
  xmlNsPtr ns = NULL;
982
1342
 
983
- Data_Get_Struct(self, xmlNode, node);
1343
+ Noko_Node_Get_Struct(self, xmlNode, node);
984
1344
 
985
1345
  if (!NIL_P(namespace)) {
986
- Data_Get_Struct(namespace, xmlNs, ns);
1346
+ Noko_Namespace_Get_Struct(namespace, xmlNs, ns);
987
1347
  }
988
1348
 
989
1349
  xmlSetNs(node, ns);
@@ -992,70 +1352,32 @@ set_namespace(VALUE self, VALUE namespace)
992
1352
  }
993
1353
 
994
1354
  /*
995
- * call-seq:
996
- * attribute(name)
1355
+ * :call-seq:
1356
+ * namespace() → Namespace
997
1357
  *
998
- * Get the attribute node with +name+
999
- */
1000
- static VALUE
1001
- attr(VALUE self, VALUE name)
1002
- {
1003
- xmlNodePtr node;
1004
- xmlAttrPtr prop;
1005
- Data_Get_Struct(self, xmlNode, node);
1006
- prop = xmlHasProp(node, (xmlChar *)StringValueCStr(name));
1007
-
1008
- if (! prop) { return Qnil; }
1009
- return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
1010
- }
1011
-
1012
- /*
1013
- * call-seq:
1014
- * attribute_with_ns(name, namespace)
1358
+ * [Returns] The Namespace of the element or attribute node, or +nil+ if there is no namespace.
1015
1359
  *
1016
- * Get the attribute node with +name+ and +namespace+
1017
- */
1018
- static VALUE
1019
- attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
1020
- {
1021
- xmlNodePtr node;
1022
- xmlAttrPtr prop;
1023
- Data_Get_Struct(self, xmlNode, node);
1024
- prop = xmlHasNsProp(node, (xmlChar *)StringValueCStr(name),
1025
- NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace));
1026
-
1027
- if (! prop) { return Qnil; }
1028
- return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
1029
- }
1030
-
1031
- /*
1032
- * @overload attribute_nodes()
1033
- * Get the attributes for a Node
1034
- * @return [Array<Nokogiri::XML::Attr>] containing the Node's attributes.
1035
- */
1036
- static VALUE
1037
- attribute_nodes(VALUE rb_node)
1038
- {
1039
- xmlNodePtr c_node;
1040
-
1041
- Data_Get_Struct(rb_node, xmlNode, c_node);
1042
-
1043
- return noko_xml_node_attrs(c_node);
1044
- }
1045
-
1046
-
1047
- /*
1048
- * call-seq:
1049
- * namespace()
1360
+ * *Example:*
1050
1361
  *
1051
- * returns the namespace of the element or attribute node as a Namespace
1052
- * object, or nil if there is no namespace for the element or attribute.
1362
+ * doc = Nokogiri::XML(<<~EOF)
1363
+ * <root>
1364
+ * <first/>
1365
+ * <second xmlns="http://example.com/child"/>
1366
+ * <foo:third xmlns:foo="http://example.com/foo"/>
1367
+ * </root>
1368
+ * EOF
1369
+ * doc.at_xpath("//first").namespace
1370
+ * # => nil
1371
+ * doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace
1372
+ * # => #(Namespace:0x3c { href = "http://example.com/child" })
1373
+ * doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace
1374
+ * # => #(Namespace:0x50 { prefix = "foo", href = "http://example.com/foo" })
1053
1375
  */
1054
1376
  static VALUE
1055
- noko_xml_node_namespace(VALUE rb_node)
1377
+ rb_xml_node_namespace(VALUE rb_node)
1056
1378
  {
1057
1379
  xmlNodePtr c_node ;
1058
- Data_Get_Struct(rb_node, xmlNode, c_node);
1380
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1059
1381
 
1060
1382
  if (c_node->ns) {
1061
1383
  return noko_xml_namespace_wrap(c_node->ns, c_node->doc);
@@ -1065,10 +1387,32 @@ noko_xml_node_namespace(VALUE rb_node)
1065
1387
  }
1066
1388
 
1067
1389
  /*
1068
- * call-seq:
1069
- * namespace_definitions()
1390
+ * :call-seq:
1391
+ * namespace_definitions() → Array<Nokogiri::XML::Namespace>
1392
+ *
1393
+ * [Returns]
1394
+ * Namespaces that are defined directly on this node, as an Array of Namespace objects. The array
1395
+ * will be empty if no namespaces are defined on this node.
1070
1396
  *
1071
- * returns namespaces defined on self element directly, as an array of Namespace objects. Includes both a default namespace (as in"xmlns="), and prefixed namespaces (as in "xmlns:prefix=").
1397
+ * *Example:*
1398
+ *
1399
+ * doc = Nokogiri::XML(<<~EOF)
1400
+ * <root xmlns="http://example.com/root">
1401
+ * <first/>
1402
+ * <second xmlns="http://example.com/child" xmlns:unused="http://example.com/unused"/>
1403
+ * <foo:third xmlns:foo="http://example.com/foo"/>
1404
+ * </root>
1405
+ * EOF
1406
+ * doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_definitions
1407
+ * # => []
1408
+ * doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace_definitions
1409
+ * # => [#(Namespace:0x3c { href = "http://example.com/child" }),
1410
+ * # #(Namespace:0x50 {
1411
+ * # prefix = "unused",
1412
+ * # href = "http://example.com/unused"
1413
+ * # })]
1414
+ * doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace_definitions
1415
+ * # => [#(Namespace:0x64 { prefix = "foo", href = "http://example.com/foo" })]
1072
1416
  */
1073
1417
  static VALUE
1074
1418
  namespace_definitions(VALUE rb_node)
@@ -1078,7 +1422,7 @@ namespace_definitions(VALUE rb_node)
1078
1422
  xmlNsPtr c_namespace;
1079
1423
  VALUE definitions = rb_ary_new();
1080
1424
 
1081
- Data_Get_Struct(rb_node, xmlNode, c_node);
1425
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1082
1426
 
1083
1427
  c_namespace = c_node->nsDef;
1084
1428
  if (!c_namespace) {
@@ -1094,23 +1438,42 @@ namespace_definitions(VALUE rb_node)
1094
1438
  }
1095
1439
 
1096
1440
  /*
1097
- * call-seq:
1098
- * namespace_scopes()
1441
+ * :call-seq:
1442
+ * namespace_scopes() → Array<Nokogiri::XML::Namespace>
1443
+ *
1444
+ * [Returns] Array of all the Namespaces on this node and its ancestors.
1099
1445
  *
1100
- * returns namespaces in scope for self -- those defined on self element
1101
- * directly or any ancestor node -- as an array of Namespace objects. Default
1102
- * namespaces ("xmlns=" style) for self are included in this array; Default
1103
- * namespaces for ancestors, however, are not. See also #namespaces
1446
+ * See also #namespaces
1447
+ *
1448
+ * *Example:*
1449
+ *
1450
+ * doc = Nokogiri::XML(<<~EOF)
1451
+ * <root xmlns="http://example.com/root" xmlns:bar="http://example.com/bar">
1452
+ * <first/>
1453
+ * <second xmlns="http://example.com/child"/>
1454
+ * <third xmlns:foo="http://example.com/foo"/>
1455
+ * </root>
1456
+ * EOF
1457
+ * doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_scopes
1458
+ * # => [#(Namespace:0x3c { href = "http://example.com/root" }),
1459
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1460
+ * doc.at_xpath("//child:second", "child" => "http://example.com/child").namespace_scopes
1461
+ * # => [#(Namespace:0x64 { href = "http://example.com/child" }),
1462
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1463
+ * doc.at_xpath("//root:third", "root" => "http://example.com/root").namespace_scopes
1464
+ * # => [#(Namespace:0x78 { prefix = "foo", href = "http://example.com/foo" }),
1465
+ * # #(Namespace:0x3c { href = "http://example.com/root" }),
1466
+ * # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
1104
1467
  */
1105
1468
  static VALUE
1106
- namespace_scopes(VALUE rb_node)
1469
+ rb_xml_node_namespace_scopes(VALUE rb_node)
1107
1470
  {
1108
1471
  xmlNodePtr c_node ;
1109
1472
  xmlNsPtr *namespaces;
1110
1473
  VALUE scopes = rb_ary_new();
1111
1474
  int j;
1112
1475
 
1113
- Data_Get_Struct(rb_node, xmlNode, c_node);
1476
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1114
1477
 
1115
1478
  namespaces = xmlGetNsList(c_node->doc, c_node);
1116
1479
  if (!namespaces) {
@@ -1135,8 +1498,8 @@ static VALUE
1135
1498
  node_type(VALUE self)
1136
1499
  {
1137
1500
  xmlNodePtr node;
1138
- Data_Get_Struct(self, xmlNode, node);
1139
- return INT2NUM((long)node->type);
1501
+ Noko_Node_Get_Struct(self, xmlNode, node);
1502
+ return INT2NUM(node->type);
1140
1503
  }
1141
1504
 
1142
1505
  /*
@@ -1149,7 +1512,7 @@ static VALUE
1149
1512
  set_native_content(VALUE self, VALUE content)
1150
1513
  {
1151
1514
  xmlNodePtr node, child, next ;
1152
- Data_Get_Struct(self, xmlNode, node);
1515
+ Noko_Node_Get_Struct(self, xmlNode, node);
1153
1516
 
1154
1517
  child = node->children;
1155
1518
  while (NULL != child) {
@@ -1163,30 +1526,6 @@ set_native_content(VALUE self, VALUE content)
1163
1526
  return content;
1164
1527
  }
1165
1528
 
1166
- /*
1167
- * call-seq:
1168
- * content
1169
- *
1170
- * Returns the plaintext content for this Node. Note that entities will always
1171
- * be expanded in the returned string.
1172
- */
1173
- static VALUE
1174
- get_native_content(VALUE self)
1175
- {
1176
- xmlNodePtr node;
1177
- xmlChar *content;
1178
-
1179
- Data_Get_Struct(self, xmlNode, node);
1180
-
1181
- content = xmlNodeGetContent(node);
1182
- if (content) {
1183
- VALUE rval = NOKOGIRI_STR_NEW2(content);
1184
- xmlFree(content);
1185
- return rval;
1186
- }
1187
- return Qnil;
1188
- }
1189
-
1190
1529
  /*
1191
1530
  * call-seq:
1192
1531
  * lang=
@@ -1199,7 +1538,7 @@ set_lang(VALUE self_rb, VALUE lang_rb)
1199
1538
  xmlNodePtr self ;
1200
1539
  xmlChar *lang ;
1201
1540
 
1202
- Data_Get_Struct(self_rb, xmlNode, self);
1541
+ Noko_Node_Get_Struct(self_rb, xmlNode, self);
1203
1542
  lang = (xmlChar *)StringValueCStr(lang_rb);
1204
1543
 
1205
1544
  xmlNodeSetLang(self, lang);
@@ -1221,7 +1560,7 @@ get_lang(VALUE self_rb)
1221
1560
  xmlChar *lang ;
1222
1561
  VALUE lang_rb ;
1223
1562
 
1224
- Data_Get_Struct(self_rb, xmlNode, self);
1563
+ Noko_Node_Get_Struct(self_rb, xmlNode, self);
1225
1564
 
1226
1565
  lang = xmlNodeGetLang(self);
1227
1566
  if (lang) {
@@ -1250,7 +1589,7 @@ static VALUE
1250
1589
  get_parent(VALUE self)
1251
1590
  {
1252
1591
  xmlNodePtr node, parent;
1253
- Data_Get_Struct(self, xmlNode, node);
1592
+ Noko_Node_Get_Struct(self, xmlNode, node);
1254
1593
 
1255
1594
  parent = node->parent;
1256
1595
  if (!parent) { return Qnil; }
@@ -1268,7 +1607,7 @@ static VALUE
1268
1607
  set_name(VALUE self, VALUE new_name)
1269
1608
  {
1270
1609
  xmlNodePtr node;
1271
- Data_Get_Struct(self, xmlNode, node);
1610
+ Noko_Node_Get_Struct(self, xmlNode, node);
1272
1611
  xmlNodeSetName(node, (xmlChar *)StringValueCStr(new_name));
1273
1612
  return new_name;
1274
1613
  }
@@ -1283,7 +1622,7 @@ static VALUE
1283
1622
  get_name(VALUE self)
1284
1623
  {
1285
1624
  xmlNodePtr node;
1286
- Data_Get_Struct(self, xmlNode, node);
1625
+ Noko_Node_Get_Struct(self, xmlNode, node);
1287
1626
  if (node->name) {
1288
1627
  return NOKOGIRI_STR_NEW2(node->name);
1289
1628
  }
@@ -1297,13 +1636,13 @@ get_name(VALUE self)
1297
1636
  * Returns the path associated with this Node
1298
1637
  */
1299
1638
  static VALUE
1300
- noko_xml_node_path(VALUE rb_node)
1639
+ rb_xml_node_path(VALUE rb_node)
1301
1640
  {
1302
1641
  xmlNodePtr c_node;
1303
1642
  xmlChar *c_path ;
1304
1643
  VALUE rval;
1305
1644
 
1306
- Data_Get_Struct(rb_node, xmlNode, c_node);
1645
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1307
1646
 
1308
1647
  c_path = xmlGetNodePath(c_node);
1309
1648
  if (c_path == NULL) {
@@ -1352,7 +1691,7 @@ native_write_to(
1352
1691
  const char *before_indent;
1353
1692
  xmlSaveCtxtPtr savectx;
1354
1693
 
1355
- Data_Get_Struct(self, xmlNode, node);
1694
+ Noko_Node_Get_Struct(self, xmlNode, node);
1356
1695
 
1357
1696
  xmlIndentTreeOutput = 1;
1358
1697
 
@@ -1375,112 +1714,362 @@ native_write_to(
1375
1714
  return io;
1376
1715
  }
1377
1716
 
1378
- /*
1379
- * call-seq:
1380
- * line
1381
- *
1382
- * Returns the line for this Node
1383
- */
1717
+
1718
+ static inline void
1719
+ output_partial_string(VALUE out, char const *str, size_t length)
1720
+ {
1721
+ if (length) {
1722
+ rb_enc_str_buf_cat(out, str, (long)length, rb_utf8_encoding());
1723
+ }
1724
+ }
1725
+
1726
+ static inline void
1727
+ output_char(VALUE out, char ch)
1728
+ {
1729
+ output_partial_string(out, &ch, 1);
1730
+ }
1731
+
1732
+ static inline void
1733
+ output_string(VALUE out, char const *str)
1734
+ {
1735
+ output_partial_string(out, str, strlen(str));
1736
+ }
1737
+
1738
+ static inline void
1739
+ output_tagname(VALUE out, xmlNodePtr elem)
1740
+ {
1741
+ // Elements in the HTML, MathML, and SVG namespaces do not use a namespace
1742
+ // prefix in the HTML syntax.
1743
+ char const *name = (char const *)elem->name;
1744
+ xmlNsPtr ns = elem->ns;
1745
+ if (ns && ns->href && ns->prefix
1746
+ && strcmp((char const *)ns->href, "http://www.w3.org/1999/xhtml")
1747
+ && strcmp((char const *)ns->href, "http://www.w3.org/1998/Math/MathML")
1748
+ && strcmp((char const *)ns->href, "http://www.w3.org/2000/svg")) {
1749
+ output_string(out, (char const *)elem->ns->prefix);
1750
+ output_char(out, ':');
1751
+ char const *colon = strchr(name, ':');
1752
+ if (colon) {
1753
+ name = colon + 1;
1754
+ }
1755
+ }
1756
+ output_string(out, name);
1757
+ }
1758
+
1759
+ static inline void
1760
+ output_attr_name(VALUE out, xmlAttrPtr attr)
1761
+ {
1762
+ xmlNsPtr ns = attr->ns;
1763
+ char const *name = (char const *)attr->name;
1764
+ if (ns && ns->href) {
1765
+ char const *uri = (char const *)ns->href;
1766
+ char const *localname = strchr(name, ':');
1767
+ if (localname) {
1768
+ ++localname;
1769
+ } else {
1770
+ localname = name;
1771
+ }
1772
+
1773
+ if (!strcmp(uri, "http://www.w3.org/XML/1998/namespace")) {
1774
+ output_string(out, "xml:");
1775
+ name = localname;
1776
+ } else if (!strcmp(uri, "http://www.w3.org/2000/xmlns/")) {
1777
+ // xmlns:xmlns -> xmlns
1778
+ // xmlns:foo -> xmlns:foo
1779
+ if (strcmp(localname, "xmlns")) {
1780
+ output_string(out, "xmlns:");
1781
+ }
1782
+ name = localname;
1783
+ } else if (!strcmp(uri, "http://www.w3.org/1999/xlink")) {
1784
+ output_string(out, "xlink:");
1785
+ name = localname;
1786
+ } else if (ns->prefix) {
1787
+ output_string(out, (char const *)ns->prefix);
1788
+ output_char(out, ':');
1789
+ name = localname;
1790
+ }
1791
+ }
1792
+ output_string(out, name);
1793
+ }
1794
+
1795
+ static void
1796
+ output_escaped_string(VALUE out, xmlChar const *start, bool attr)
1797
+ {
1798
+ xmlChar const *next = start;
1799
+ int ch;
1800
+
1801
+ while ((ch = *next) != 0) {
1802
+ char const *replacement = NULL;
1803
+ size_t replaced_bytes = 1;
1804
+ if (ch == '&') {
1805
+ replacement = "&amp;";
1806
+ } else if (ch == 0xC2 && next[1] == 0xA0) {
1807
+ // U+00A0 NO-BREAK SPACE has the UTF-8 encoding C2 A0.
1808
+ replacement = "&nbsp;";
1809
+ replaced_bytes = 2;
1810
+ } else if (attr && ch == '"') {
1811
+ replacement = "&quot;";
1812
+ } else if (!attr && ch == '<') {
1813
+ replacement = "&lt;";
1814
+ } else if (!attr && ch == '>') {
1815
+ replacement = "&gt;";
1816
+ } else {
1817
+ ++next;
1818
+ continue;
1819
+ }
1820
+ output_partial_string(out, (char const *)start, next - start);
1821
+ output_string(out, replacement);
1822
+ next += replaced_bytes;
1823
+ start = next;
1824
+ }
1825
+ output_partial_string(out, (char const *)start, next - start);
1826
+ }
1827
+
1828
+ static bool
1829
+ should_prepend_newline(xmlNodePtr node)
1830
+ {
1831
+ char const *name = (char const *)node->name;
1832
+ xmlNodePtr child = node->children;
1833
+
1834
+ if (!name || !child || (strcmp(name, "pre") && strcmp(name, "textarea") && strcmp(name, "listing"))) {
1835
+ return false;
1836
+ }
1837
+
1838
+ return child->type == XML_TEXT_NODE && child->content && child->content[0] == '\n';
1839
+ }
1840
+
1384
1841
  static VALUE
1385
- line(VALUE self)
1842
+ rb_prepend_newline(VALUE self)
1386
1843
  {
1387
1844
  xmlNodePtr node;
1388
- Data_Get_Struct(self, xmlNode, node);
1845
+ Noko_Node_Get_Struct(self, xmlNode, node);
1846
+ return should_prepend_newline(node) ? Qtrue : Qfalse;
1847
+ }
1848
+
1849
+ static bool
1850
+ is_one_of(xmlNodePtr node, char const *const *tagnames, size_t num_tagnames)
1851
+ {
1852
+ char const *name = (char const *)node->name;
1853
+ if (name == NULL) { // fragments don't have a name
1854
+ return false;
1855
+ }
1856
+ for (size_t idx = 0; idx < num_tagnames; ++idx) {
1857
+ if (!strcmp(name, tagnames[idx])) {
1858
+ return true;
1859
+ }
1860
+ }
1861
+ return false;
1862
+
1863
+ }
1864
+
1865
+ static void
1866
+ output_node(
1867
+ VALUE out,
1868
+ xmlNodePtr node,
1869
+ bool preserve_newline
1870
+ )
1871
+ {
1872
+ static char const *const VOID_ELEMENTS[] = {
1873
+ "area", "base", "basefont", "bgsound", "br", "col", "embed", "frame", "hr",
1874
+ "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr",
1875
+ };
1876
+
1877
+ static char const *const UNESCAPED_TEXT_ELEMENTS[] = {
1878
+ "style", "script", "xmp", "iframe", "noembed", "noframes", "plaintext", "noscript",
1879
+ };
1880
+
1881
+ switch (node->type) {
1882
+ case XML_ELEMENT_NODE:
1883
+ // Serialize the start tag.
1884
+ output_char(out, '<');
1885
+ output_tagname(out, node);
1886
+
1887
+ // Add attributes.
1888
+ for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
1889
+ output_char(out, ' ');
1890
+ output_attr_name(out, attr);
1891
+ if (attr->children) {
1892
+ output_string(out, "=\"");
1893
+ xmlChar *value = xmlNodeListGetString(attr->doc, attr->children, 1);
1894
+ output_escaped_string(out, value, true);
1895
+ xmlFree(value);
1896
+ output_char(out, '"');
1897
+ } else {
1898
+ // Output name=""
1899
+ output_string(out, "=\"\"");
1900
+ }
1901
+ }
1902
+ output_char(out, '>');
1903
+
1904
+ // Add children and end tag if element is not void.
1905
+ if (!is_one_of(node, VOID_ELEMENTS, sizeof VOID_ELEMENTS / sizeof VOID_ELEMENTS[0])) {
1906
+ if (preserve_newline && should_prepend_newline(node)) {
1907
+ output_char(out, '\n');
1908
+ }
1909
+ for (xmlNodePtr child = node->children; child; child = child->next) {
1910
+ output_node(out, child, preserve_newline);
1911
+ }
1912
+ output_string(out, "</");
1913
+ output_tagname(out, node);
1914
+ output_char(out, '>');
1915
+ }
1916
+ break;
1917
+
1918
+ case XML_TEXT_NODE:
1919
+ if (node->parent
1920
+ && is_one_of(node->parent, UNESCAPED_TEXT_ELEMENTS,
1921
+ sizeof UNESCAPED_TEXT_ELEMENTS / sizeof UNESCAPED_TEXT_ELEMENTS[0])) {
1922
+ output_string(out, (char const *)node->content);
1923
+ } else {
1924
+ output_escaped_string(out, node->content, false);
1925
+ }
1926
+ break;
1927
+
1928
+ case XML_CDATA_SECTION_NODE:
1929
+ output_string(out, "<![CDATA[");
1930
+ output_string(out, (char const *)node->content);
1931
+ output_string(out, "]]>");
1932
+ break;
1933
+
1934
+ case XML_COMMENT_NODE:
1935
+ output_string(out, "<!--");
1936
+ output_string(out, (char const *)node->content);
1937
+ output_string(out, "-->");
1938
+ break;
1939
+
1940
+ case XML_PI_NODE:
1941
+ output_string(out, "<?");
1942
+ output_string(out, (char const *)node->content);
1943
+ output_char(out, '>');
1944
+ break;
1945
+
1946
+ case XML_DOCUMENT_TYPE_NODE:
1947
+ case XML_DTD_NODE:
1948
+ output_string(out, "<!DOCTYPE ");
1949
+ output_string(out, (char const *)node->name);
1950
+ output_string(out, ">");
1951
+ break;
1952
+
1953
+ case XML_DOCUMENT_NODE:
1954
+ case XML_DOCUMENT_FRAG_NODE:
1955
+ case XML_HTML_DOCUMENT_NODE:
1956
+ for (xmlNodePtr child = node->children; child; child = child->next) {
1957
+ output_node(out, child, preserve_newline);
1958
+ }
1959
+ break;
1960
+
1961
+ default:
1962
+ rb_raise(rb_eRuntimeError, "Unsupported document node (%d); this is a bug in Nokogiri", node->type);
1963
+ break;
1964
+ }
1965
+ }
1389
1966
 
1390
- return INT2NUM(xmlGetLineNo(node));
1967
+ static VALUE
1968
+ html_standard_serialize(
1969
+ VALUE self,
1970
+ VALUE preserve_newline
1971
+ )
1972
+ {
1973
+ xmlNodePtr node;
1974
+ Noko_Node_Get_Struct(self, xmlNode, node);
1975
+ VALUE output = rb_str_buf_new(4096);
1976
+ output_node(output, node, RTEST(preserve_newline));
1977
+ return output;
1391
1978
  }
1392
1979
 
1393
1980
  /*
1394
- * call-seq:
1395
- * line=(num)
1981
+ * :call-seq:
1982
+ * line() → Integer
1396
1983
  *
1397
- * Sets the line for this Node. num must be less than 65535.
1984
+ * [Returns] The line number of this Node.
1985
+ *
1986
+ * ---
1987
+ *
1988
+ * <b> ⚠ The CRuby and JRuby implementations differ in important ways! </b>
1989
+ *
1990
+ * Semantic differences:
1991
+ * - The CRuby method reflects the node's line number <i>in the parsed string</i>
1992
+ * - The JRuby method reflects the node's line number <i>in the final DOM structure</i> after
1993
+ * corrections have been applied
1994
+ *
1995
+ * Performance differences:
1996
+ * - The CRuby method is {O(1)}[https://en.wikipedia.org/wiki/Time_complexity#Constant_time]
1997
+ * (constant time)
1998
+ * - The JRuby method is {O(n)}[https://en.wikipedia.org/wiki/Time_complexity#Linear_time] (linear
1999
+ * time, where n is the number of nodes before/above the element in the DOM)
2000
+ *
2001
+ * If you'd like to help improve the JRuby implementation, please review these issues and reach out
2002
+ * to the maintainers:
2003
+ * - https://github.com/sparklemotion/nokogiri/issues/1223
2004
+ * - https://github.com/sparklemotion/nokogiri/pull/2177
2005
+ * - https://github.com/sparklemotion/nokogiri/issues/2380
1398
2006
  */
1399
2007
  static VALUE
1400
- set_line(VALUE self, VALUE num)
2008
+ rb_xml_node_line(VALUE rb_node)
1401
2009
  {
1402
- xmlNodePtr node;
1403
- int value = NUM2INT(num);
1404
-
1405
- Data_Get_Struct(self, xmlNode, node);
1406
- if (value < 65535) {
1407
- node->line = value;
1408
- }
2010
+ xmlNodePtr c_node;
2011
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1409
2012
 
1410
- return num;
2013
+ return LONG2NUM(xmlGetLineNo(c_node));
1411
2014
  }
1412
2015
 
1413
2016
  /*
1414
2017
  * call-seq:
1415
- * add_namespace_definition(prefix, href)
1416
- *
1417
- * Adds a namespace definition with +prefix+ using +href+ value. The result is
1418
- * as if parsed XML for this node had included an attribute
1419
- * 'xmlns:prefix=value'. A default namespace for this node ("xmlns=") can be
1420
- * added by passing 'nil' for prefix. Namespaces added this way will not
1421
- * show up in #attributes, but they will be included as an xmlns attribute
1422
- * when the node is serialized to XML.
2018
+ * line=(num)
2019
+ *
2020
+ * Sets the line for this Node. num must be less than 65535.
1423
2021
  */
1424
2022
  static VALUE
1425
- add_namespace_definition(VALUE rb_node, VALUE rb_prefix, VALUE rb_href)
2023
+ rb_xml_node_line_set(VALUE rb_node, VALUE rb_line_number)
1426
2024
  {
1427
- xmlNodePtr c_node, element;
1428
- xmlNsPtr c_namespace;
1429
- const xmlChar *c_prefix = (const xmlChar *)(NIL_P(rb_prefix) ? NULL : StringValueCStr(rb_prefix));
1430
-
1431
- Data_Get_Struct(rb_node, xmlNode, c_node);
1432
- element = c_node ;
2025
+ xmlNodePtr c_node;
2026
+ int line_number = NUM2INT(rb_line_number);
1433
2027
 
1434
- c_namespace = xmlSearchNs(c_node->doc, c_node, c_prefix);
2028
+ Noko_Node_Get_Struct(rb_node, xmlNode, c_node);
1435
2029
 
1436
- if (!c_namespace) {
1437
- if (c_node->type != XML_ELEMENT_NODE) {
1438
- element = c_node->parent;
2030
+ // libxml2 optionally uses xmlNode.psvi to store longer line numbers, but only for text nodes.
2031
+ // search for "psvi" in SAX2.c and tree.c to learn more.
2032
+ if (line_number < 65535) {
2033
+ c_node->line = (short) line_number;
2034
+ } else {
2035
+ c_node->line = 65535;
2036
+ if (c_node->type == XML_TEXT_NODE) {
2037
+ c_node->psvi = (void *)(ptrdiff_t) line_number;
1439
2038
  }
1440
- c_namespace = xmlNewNs(element, (const xmlChar *)StringValueCStr(rb_href), c_prefix);
1441
- }
1442
-
1443
- if (!c_namespace) {
1444
- return Qnil ;
1445
- }
1446
-
1447
- if (NIL_P(rb_prefix) || c_node != element) {
1448
- xmlSetNs(c_node, c_namespace);
1449
2039
  }
1450
2040
 
1451
- return noko_xml_namespace_wrap(c_namespace, c_node->doc);
2041
+ return rb_line_number;
1452
2042
  }
1453
2043
 
1454
- /*
1455
- * @overload new(name, document)
1456
- * Create a new node with +name+ sharing GC lifecycle with +document+.
1457
- * @param name [String]
1458
- * @param document [Nokogiri::XML::Document]
1459
- * @yieldparam node [Nokogiri::XML::Node]
1460
- * @return [Nokogiri::XML::Node]
1461
- * @see Nokogiri::XML::Node#initialize
1462
- */
2044
+ /* :nodoc: documented in lib/nokogiri/xml/node.rb */
1463
2045
  static VALUE
1464
2046
  rb_xml_node_new(int argc, VALUE *argv, VALUE klass)
1465
2047
  {
1466
- xmlDocPtr doc;
1467
- xmlNodePtr node;
1468
- VALUE name;
1469
- VALUE document;
2048
+ xmlNodePtr c_document_node;
2049
+ xmlNodePtr c_node;
2050
+ VALUE rb_name;
2051
+ VALUE rb_document_node;
1470
2052
  VALUE rest;
1471
2053
  VALUE rb_node;
1472
2054
 
1473
- rb_scan_args(argc, argv, "2*", &name, &document, &rest);
2055
+ rb_scan_args(argc, argv, "2*", &rb_name, &rb_document_node, &rest);
1474
2056
 
1475
- Data_Get_Struct(document, xmlDoc, doc);
2057
+ if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlNode)) {
2058
+ rb_raise(rb_eArgError, "document must be a Nokogiri::XML::Node");
2059
+ }
2060
+ if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlDocument)) {
2061
+ // TODO: deprecate allowing Node
2062
+ NOKO_WARN_DEPRECATION("Passing a Node as the second parameter to Node.new is deprecated. Please pass a Document instead, or prefer an alternative constructor like Node#add_child. This will become an error in a future release of Nokogiri.");
2063
+ }
2064
+ Noko_Node_Get_Struct(rb_document_node, xmlNode, c_document_node);
1476
2065
 
1477
- node = xmlNewNode(NULL, (xmlChar *)StringValueCStr(name));
1478
- node->doc = doc->doc;
1479
- noko_xml_document_pin_node(node);
2066
+ c_node = xmlNewNode(NULL, (xmlChar *)StringValueCStr(rb_name));
2067
+ c_node->doc = c_document_node->doc;
2068
+ noko_xml_document_pin_node(c_node);
1480
2069
 
1481
2070
  rb_node = noko_xml_node_wrap(
1482
2071
  klass == cNokogiriXmlNode ? (VALUE)NULL : klass,
1483
- node
2072
+ c_node
1484
2073
  );
1485
2074
  rb_obj_call_init(rb_node, argc, argv);
1486
2075
 
@@ -1502,7 +2091,7 @@ dump_html(VALUE self)
1502
2091
  xmlNodePtr node ;
1503
2092
  VALUE html;
1504
2093
 
1505
- Data_Get_Struct(self, xmlNode, node);
2094
+ Noko_Node_Get_Struct(self, xmlNode, node);
1506
2095
 
1507
2096
  buf = xmlBufferCreate() ;
1508
2097
  htmlNodeDump(buf, node->doc, node);
@@ -1521,10 +2110,10 @@ static VALUE
1521
2110
  compare(VALUE self, VALUE _other)
1522
2111
  {
1523
2112
  xmlNodePtr node, other;
1524
- Data_Get_Struct(self, xmlNode, node);
1525
- Data_Get_Struct(_other, xmlNode, other);
2113
+ Noko_Node_Get_Struct(self, xmlNode, node);
2114
+ Noko_Node_Get_Struct(_other, xmlNode, other);
1526
2115
 
1527
- return INT2NUM((long)xmlXPathCmpNodes(other, node));
2116
+ return INT2NUM(xmlXPathCmpNodes(other, node));
1528
2117
  }
1529
2118
 
1530
2119
 
@@ -1542,7 +2131,7 @@ process_xincludes(VALUE self, VALUE options)
1542
2131
  xmlNodePtr node;
1543
2132
  VALUE error_list = rb_ary_new();
1544
2133
 
1545
- Data_Get_Struct(self, xmlNode, node);
2134
+ Noko_Node_Get_Struct(self, xmlNode, node);
1546
2135
 
1547
2136
  xmlSetStructuredErrorFunc((void *)error_list, Nokogiri_error_array_pusher);
1548
2137
  rcode = xmlXIncludeProcessTreeFlags(node, (int)NUM2INT(options));
@@ -1573,7 +2162,7 @@ in_context(VALUE self, VALUE _str, VALUE _options)
1573
2162
  VALUE doc, err;
1574
2163
  int doc_is_empty;
1575
2164
 
1576
- Data_Get_Struct(self, xmlNode, node);
2165
+ Noko_Node_Get_Struct(self, xmlNode, node);
1577
2166
 
1578
2167
  doc = DOC_RUBY_OBJECT(node->doc);
1579
2168
  err = rb_iv_get(doc, "@errors");
@@ -1614,9 +2203,7 @@ in_context(VALUE self, VALUE _str, VALUE _options)
1614
2203
  */
1615
2204
  child_iter = node->doc->children ;
1616
2205
  while (child_iter) {
1617
- if (child_iter->parent != (xmlNodePtr)node->doc) {
1618
- child_iter->parent = (xmlNodePtr)node->doc;
1619
- }
2206
+ child_iter->parent = (xmlNodePtr)node->doc;
1620
2207
  child_iter = child_iter->next;
1621
2208
  }
1622
2209
 
@@ -1626,12 +2213,17 @@ in_context(VALUE self, VALUE _str, VALUE _options)
1626
2213
 
1627
2214
  xmlSetStructuredErrorFunc(NULL, NULL);
1628
2215
 
1629
- /* Workaround for a libxml2 bug where a parsing error may leave a broken
2216
+ /*
2217
+ * Workaround for a libxml2 bug where a parsing error may leave a broken
1630
2218
  * node reference in node->doc->children.
2219
+ *
2220
+ * https://bugzilla.gnome.org/show_bug.cgi?id=668155
2221
+ *
1631
2222
  * This workaround is limited to when a parse error occurs, the document
1632
2223
  * went from having no children to having children, and the context node is
1633
2224
  * part of a document fragment.
1634
- * https://bugzilla.gnome.org/show_bug.cgi?id=668155
2225
+ *
2226
+ * TODO: This was fixed in libxml 2.8.0 by 71a243d
1635
2227
  */
1636
2228
  if (error != XML_ERR_OK && doc_is_empty && node->doc->children != NULL) {
1637
2229
  child_iter = node;
@@ -1667,14 +2259,12 @@ in_context(VALUE self, VALUE _str, VALUE _options)
1667
2259
  return noko_xml_node_set_wrap(set, doc);
1668
2260
  }
1669
2261
 
1670
-
1671
2262
  VALUE
1672
2263
  noko_xml_node_wrap(VALUE rb_class, xmlNodePtr c_node)
1673
2264
  {
1674
2265
  VALUE rb_document, rb_node_cache, rb_node;
1675
2266
  nokogiriTuplePtr node_has_a_document;
1676
2267
  xmlDocPtr c_doc;
1677
- void (*mark_method)(xmlNodePtr) = NULL ;
1678
2268
 
1679
2269
  assert(c_node);
1680
2270
 
@@ -1682,11 +2272,9 @@ noko_xml_node_wrap(VALUE rb_class, xmlNodePtr c_node)
1682
2272
  return DOC_RUBY_OBJECT(c_node->doc);
1683
2273
  }
1684
2274
 
1685
- /* It's OK if the node doesn't have a fully-realized document (as in XML::Reader). */
1686
- /* see https://github.com/sparklemotion/nokogiri/issues/95 */
1687
- /* and https://github.com/sparklemotion/nokogiri/issues/439 */
1688
2275
  c_doc = c_node->doc;
1689
- if (c_doc->type == XML_DOCUMENT_FRAG_NODE) { c_doc = c_doc->doc; }
2276
+
2277
+ // Nodes yielded from XML::Reader don't have a fully-realized Document
1690
2278
  node_has_a_document = DOC_RUBY_OBJECT_TEST(c_doc);
1691
2279
 
1692
2280
  if (c_node->_private && node_has_a_document) {
@@ -1736,9 +2324,7 @@ noko_xml_node_wrap(VALUE rb_class, xmlNodePtr c_node)
1736
2324
  }
1737
2325
  }
1738
2326
 
1739
- mark_method = node_has_a_document ? mark : NULL ;
1740
-
1741
- rb_node = Data_Wrap_Struct(rb_class, mark_method, debug_node_dealloc, c_node) ;
2327
+ rb_node = TypedData_Wrap_Struct(rb_class, &nokogiri_node_type, c_node) ;
1742
2328
  c_node->_private = (void *)rb_node;
1743
2329
 
1744
2330
  if (node_has_a_document) {
@@ -1771,7 +2357,7 @@ noko_xml_node_attrs(xmlNodePtr c_node)
1771
2357
  }
1772
2358
 
1773
2359
  void
1774
- noko_init_xml_node()
2360
+ noko_init_xml_node(void)
1775
2361
  {
1776
2362
  cNokogiriXmlNode = rb_define_class_under(mNokogiriXml, "Node", rb_cObject);
1777
2363
 
@@ -1779,61 +2365,61 @@ noko_init_xml_node()
1779
2365
 
1780
2366
  rb_define_singleton_method(cNokogiriXmlNode, "new", rb_xml_node_new, -1);
1781
2367
 
1782
- rb_define_method(cNokogiriXmlNode, "add_namespace_definition", add_namespace_definition, 2);
2368
+ rb_define_method(cNokogiriXmlNode, "add_namespace_definition", rb_xml_node_add_namespace_definition, 2);
2369
+ rb_define_method(cNokogiriXmlNode, "attribute", rb_xml_node_attribute, 1);
2370
+ rb_define_method(cNokogiriXmlNode, "attribute_nodes", rb_xml_node_attribute_nodes, 0);
2371
+ rb_define_method(cNokogiriXmlNode, "attribute_with_ns", rb_xml_node_attribute_with_ns, 2);
2372
+ rb_define_method(cNokogiriXmlNode, "blank?", rb_xml_node_blank_eh, 0);
2373
+ rb_define_method(cNokogiriXmlNode, "child", rb_xml_node_child, 0);
2374
+ rb_define_method(cNokogiriXmlNode, "children", rb_xml_node_children, 0);
2375
+ rb_define_method(cNokogiriXmlNode, "content", rb_xml_node_content, 0);
2376
+ rb_define_method(cNokogiriXmlNode, "create_external_subset", create_external_subset, 3);
2377
+ rb_define_method(cNokogiriXmlNode, "create_internal_subset", create_internal_subset, 3);
2378
+ rb_define_method(cNokogiriXmlNode, "document", rb_xml_node_document, 0);
2379
+ rb_define_method(cNokogiriXmlNode, "dup", duplicate_node, -1);
2380
+ rb_define_method(cNokogiriXmlNode, "element_children", rb_xml_node_element_children, 0);
2381
+ rb_define_method(cNokogiriXmlNode, "encode_special_chars", encode_special_chars, 1);
2382
+ rb_define_method(cNokogiriXmlNode, "external_subset", external_subset, 0);
2383
+ rb_define_method(cNokogiriXmlNode, "first_element_child", rb_xml_node_first_element_child, 0);
2384
+ rb_define_method(cNokogiriXmlNode, "internal_subset", internal_subset, 0);
2385
+ rb_define_method(cNokogiriXmlNode, "key?", key_eh, 1);
2386
+ rb_define_method(cNokogiriXmlNode, "lang", get_lang, 0);
2387
+ rb_define_method(cNokogiriXmlNode, "lang=", set_lang, 1);
2388
+ rb_define_method(cNokogiriXmlNode, "last_element_child", rb_xml_node_last_element_child, 0);
2389
+ rb_define_method(cNokogiriXmlNode, "line", rb_xml_node_line, 0);
2390
+ rb_define_method(cNokogiriXmlNode, "line=", rb_xml_node_line_set, 1);
2391
+ rb_define_method(cNokogiriXmlNode, "namespace", rb_xml_node_namespace, 0);
2392
+ rb_define_method(cNokogiriXmlNode, "namespace_definitions", namespace_definitions, 0);
2393
+ rb_define_method(cNokogiriXmlNode, "namespace_scopes", rb_xml_node_namespace_scopes, 0);
2394
+ rb_define_method(cNokogiriXmlNode, "namespaced_key?", namespaced_key_eh, 2);
2395
+ rb_define_method(cNokogiriXmlNode, "native_content=", set_native_content, 1);
2396
+ rb_define_method(cNokogiriXmlNode, "next_element", next_element, 0);
2397
+ rb_define_method(cNokogiriXmlNode, "next_sibling", next_sibling, 0);
1783
2398
  rb_define_method(cNokogiriXmlNode, "node_name", get_name, 0);
1784
- rb_define_method(cNokogiriXmlNode, "document", document, 0);
1785
2399
  rb_define_method(cNokogiriXmlNode, "node_name=", set_name, 1);
2400
+ rb_define_method(cNokogiriXmlNode, "node_type", node_type, 0);
1786
2401
  rb_define_method(cNokogiriXmlNode, "parent", get_parent, 0);
1787
- rb_define_method(cNokogiriXmlNode, "child", child, 0);
1788
- rb_define_method(cNokogiriXmlNode, "first_element_child", first_element_child, 0);
1789
- rb_define_method(cNokogiriXmlNode, "last_element_child", last_element_child, 0);
1790
- rb_define_method(cNokogiriXmlNode, "children", children, 0);
1791
- rb_define_method(cNokogiriXmlNode, "element_children", element_children, 0);
1792
- rb_define_method(cNokogiriXmlNode, "next_sibling", next_sibling, 0);
1793
- rb_define_method(cNokogiriXmlNode, "previous_sibling", previous_sibling, 0);
1794
- rb_define_method(cNokogiriXmlNode, "next_element", next_element, 0);
2402
+ rb_define_method(cNokogiriXmlNode, "path", rb_xml_node_path, 0);
2403
+ rb_define_method(cNokogiriXmlNode, "pointer_id", rb_xml_node_pointer_id, 0);
1795
2404
  rb_define_method(cNokogiriXmlNode, "previous_element", previous_element, 0);
1796
- rb_define_method(cNokogiriXmlNode, "node_type", node_type, 0);
1797
- rb_define_method(cNokogiriXmlNode, "path", noko_xml_node_path, 0);
1798
- rb_define_method(cNokogiriXmlNode, "key?", key_eh, 1);
1799
- rb_define_method(cNokogiriXmlNode, "namespaced_key?", namespaced_key_eh, 2);
1800
- rb_define_method(cNokogiriXmlNode, "blank?", blank_eh, 0);
1801
- rb_define_method(cNokogiriXmlNode, "attribute_nodes", attribute_nodes, 0);
1802
- rb_define_method(cNokogiriXmlNode, "attribute", attr, 1);
1803
- rb_define_method(cNokogiriXmlNode, "attribute_with_ns", attribute_with_ns, 2);
1804
- rb_define_method(cNokogiriXmlNode, "namespace", noko_xml_node_namespace, 0);
1805
- rb_define_method(cNokogiriXmlNode, "namespace_definitions", namespace_definitions, 0);
1806
- rb_define_method(cNokogiriXmlNode, "namespace_scopes", namespace_scopes, 0);
1807
- rb_define_method(cNokogiriXmlNode, "encode_special_chars", encode_special_chars, 1);
1808
- rb_define_method(cNokogiriXmlNode, "dup", duplicate_node, -1);
2405
+ rb_define_method(cNokogiriXmlNode, "previous_sibling", previous_sibling, 0);
1809
2406
  rb_define_method(cNokogiriXmlNode, "unlink", unlink_node, 0);
1810
- rb_define_method(cNokogiriXmlNode, "internal_subset", internal_subset, 0);
1811
- rb_define_method(cNokogiriXmlNode, "external_subset", external_subset, 0);
1812
- rb_define_method(cNokogiriXmlNode, "create_internal_subset", create_internal_subset, 3);
1813
- rb_define_method(cNokogiriXmlNode, "create_external_subset", create_external_subset, 3);
1814
- rb_define_method(cNokogiriXmlNode, "pointer_id", pointer_id, 0);
1815
- rb_define_method(cNokogiriXmlNode, "line", line, 0);
1816
- rb_define_method(cNokogiriXmlNode, "line=", set_line, 1);
1817
- rb_define_method(cNokogiriXmlNode, "content", get_native_content, 0);
1818
- rb_define_method(cNokogiriXmlNode, "native_content=", set_native_content, 1);
1819
- rb_define_method(cNokogiriXmlNode, "lang", get_lang, 0);
1820
- rb_define_method(cNokogiriXmlNode, "lang=", set_lang, 1);
1821
2407
 
1822
- rb_define_private_method(cNokogiriXmlNode, "process_xincludes", process_xincludes, 1);
1823
- rb_define_private_method(cNokogiriXmlNode, "in_context", in_context, 2);
1824
2408
  rb_define_private_method(cNokogiriXmlNode, "add_child_node", add_child, 1);
1825
- rb_define_private_method(cNokogiriXmlNode, "add_previous_sibling_node", add_previous_sibling, 1);
1826
2409
  rb_define_private_method(cNokogiriXmlNode, "add_next_sibling_node", add_next_sibling, 1);
1827
- rb_define_private_method(cNokogiriXmlNode, "replace_node", replace, 1);
2410
+ rb_define_private_method(cNokogiriXmlNode, "add_previous_sibling_node", add_previous_sibling, 1);
2411
+ rb_define_private_method(cNokogiriXmlNode, "compare", compare, 1);
1828
2412
  rb_define_private_method(cNokogiriXmlNode, "dump_html", dump_html, 0);
1829
- rb_define_private_method(cNokogiriXmlNode, "native_write_to", native_write_to, 4);
1830
2413
  rb_define_private_method(cNokogiriXmlNode, "get", get, 1);
2414
+ rb_define_private_method(cNokogiriXmlNode, "in_context", in_context, 2);
2415
+ rb_define_private_method(cNokogiriXmlNode, "native_write_to", native_write_to, 4);
2416
+ rb_define_private_method(cNokogiriXmlNode, "prepend_newline?", rb_prepend_newline, 0);
2417
+ rb_define_private_method(cNokogiriXmlNode, "html_standard_serialize", html_standard_serialize, 1);
2418
+ rb_define_private_method(cNokogiriXmlNode, "process_xincludes", process_xincludes, 1);
2419
+ rb_define_private_method(cNokogiriXmlNode, "replace_node", replace, 1);
1831
2420
  rb_define_private_method(cNokogiriXmlNode, "set", set, 2);
1832
2421
  rb_define_private_method(cNokogiriXmlNode, "set_namespace", set_namespace, 1);
1833
- rb_define_private_method(cNokogiriXmlNode, "compare", compare, 1);
1834
2422
 
1835
2423
  id_decorate = rb_intern("decorate");
1836
2424
  id_decorate_bang = rb_intern("decorate!");
1837
2425
  }
1838
-
1839
- /* vim: set noet sw=4 sws=4 */