nokogiri-maglev- 1.5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. data/.autotest +26 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.ja.rdoc +544 -0
  4. data/CHANGELOG.rdoc +532 -0
  5. data/Manifest.txt +283 -0
  6. data/README.ja.rdoc +106 -0
  7. data/README.rdoc +174 -0
  8. data/Rakefile +171 -0
  9. data/bin/nokogiri +53 -0
  10. data/ext/nokogiri/depend +358 -0
  11. data/ext/nokogiri/extconf.rb +124 -0
  12. data/ext/nokogiri/html_document.c +154 -0
  13. data/ext/nokogiri/html_document.h +10 -0
  14. data/ext/nokogiri/html_element_description.c +276 -0
  15. data/ext/nokogiri/html_element_description.h +10 -0
  16. data/ext/nokogiri/html_entity_lookup.c +32 -0
  17. data/ext/nokogiri/html_entity_lookup.h +8 -0
  18. data/ext/nokogiri/html_sax_parser_context.c +94 -0
  19. data/ext/nokogiri/html_sax_parser_context.h +11 -0
  20. data/ext/nokogiri/nokogiri.c +115 -0
  21. data/ext/nokogiri/nokogiri.h +160 -0
  22. data/ext/nokogiri/st.c +576 -0
  23. data/ext/nokogiri/xml_attr.c +94 -0
  24. data/ext/nokogiri/xml_attr.h +9 -0
  25. data/ext/nokogiri/xml_attribute_decl.c +70 -0
  26. data/ext/nokogiri/xml_attribute_decl.h +9 -0
  27. data/ext/nokogiri/xml_cdata.c +56 -0
  28. data/ext/nokogiri/xml_cdata.h +9 -0
  29. data/ext/nokogiri/xml_comment.c +54 -0
  30. data/ext/nokogiri/xml_comment.h +9 -0
  31. data/ext/nokogiri/xml_document.c +478 -0
  32. data/ext/nokogiri/xml_document.h +23 -0
  33. data/ext/nokogiri/xml_document_fragment.c +48 -0
  34. data/ext/nokogiri/xml_document_fragment.h +10 -0
  35. data/ext/nokogiri/xml_dtd.c +202 -0
  36. data/ext/nokogiri/xml_dtd.h +10 -0
  37. data/ext/nokogiri/xml_element_content.c +123 -0
  38. data/ext/nokogiri/xml_element_content.h +10 -0
  39. data/ext/nokogiri/xml_element_decl.c +69 -0
  40. data/ext/nokogiri/xml_element_decl.h +9 -0
  41. data/ext/nokogiri/xml_encoding_handler.c +79 -0
  42. data/ext/nokogiri/xml_encoding_handler.h +8 -0
  43. data/ext/nokogiri/xml_entity_decl.c +110 -0
  44. data/ext/nokogiri/xml_entity_decl.h +10 -0
  45. data/ext/nokogiri/xml_entity_reference.c +52 -0
  46. data/ext/nokogiri/xml_entity_reference.h +9 -0
  47. data/ext/nokogiri/xml_io.c +56 -0
  48. data/ext/nokogiri/xml_io.h +11 -0
  49. data/ext/nokogiri/xml_libxml2_hacks.c +112 -0
  50. data/ext/nokogiri/xml_libxml2_hacks.h +12 -0
  51. data/ext/nokogiri/xml_namespace.c +84 -0
  52. data/ext/nokogiri/xml_namespace.h +13 -0
  53. data/ext/nokogiri/xml_node.c +1397 -0
  54. data/ext/nokogiri/xml_node.h +13 -0
  55. data/ext/nokogiri/xml_node_set.c +418 -0
  56. data/ext/nokogiri/xml_node_set.h +9 -0
  57. data/ext/nokogiri/xml_processing_instruction.c +56 -0
  58. data/ext/nokogiri/xml_processing_instruction.h +9 -0
  59. data/ext/nokogiri/xml_reader.c +684 -0
  60. data/ext/nokogiri/xml_reader.h +10 -0
  61. data/ext/nokogiri/xml_relax_ng.c +162 -0
  62. data/ext/nokogiri/xml_relax_ng.h +9 -0
  63. data/ext/nokogiri/xml_sax_parser.c +293 -0
  64. data/ext/nokogiri/xml_sax_parser.h +39 -0
  65. data/ext/nokogiri/xml_sax_parser_context.c +199 -0
  66. data/ext/nokogiri/xml_sax_parser_context.h +10 -0
  67. data/ext/nokogiri/xml_sax_push_parser.c +115 -0
  68. data/ext/nokogiri/xml_sax_push_parser.h +9 -0
  69. data/ext/nokogiri/xml_schema.c +205 -0
  70. data/ext/nokogiri/xml_schema.h +9 -0
  71. data/ext/nokogiri/xml_syntax_error.c +58 -0
  72. data/ext/nokogiri/xml_syntax_error.h +13 -0
  73. data/ext/nokogiri/xml_text.c +50 -0
  74. data/ext/nokogiri/xml_text.h +9 -0
  75. data/ext/nokogiri/xml_xpath_context.c +315 -0
  76. data/ext/nokogiri/xml_xpath_context.h +9 -0
  77. data/ext/nokogiri/xslt_stylesheet.c +265 -0
  78. data/ext/nokogiri/xslt_stylesheet.h +9 -0
  79. data/lib/nokogiri.rb +127 -0
  80. data/lib/nokogiri/css.rb +27 -0
  81. data/lib/nokogiri/css/node.rb +99 -0
  82. data/lib/nokogiri/css/parser.rb +677 -0
  83. data/lib/nokogiri/css/parser.y +237 -0
  84. data/lib/nokogiri/css/parser_extras.rb +91 -0
  85. data/lib/nokogiri/css/syntax_error.rb +7 -0
  86. data/lib/nokogiri/css/tokenizer.rb +152 -0
  87. data/lib/nokogiri/css/tokenizer.rex +55 -0
  88. data/lib/nokogiri/css/xpath_visitor.rb +171 -0
  89. data/lib/nokogiri/decorators/slop.rb +35 -0
  90. data/lib/nokogiri/html.rb +36 -0
  91. data/lib/nokogiri/html/builder.rb +35 -0
  92. data/lib/nokogiri/html/document.rb +213 -0
  93. data/lib/nokogiri/html/document_fragment.rb +41 -0
  94. data/lib/nokogiri/html/element_description.rb +23 -0
  95. data/lib/nokogiri/html/element_description_defaults.rb +671 -0
  96. data/lib/nokogiri/html/entity_lookup.rb +13 -0
  97. data/lib/nokogiri/html/sax/parser.rb +52 -0
  98. data/lib/nokogiri/html/sax/parser_context.rb +16 -0
  99. data/lib/nokogiri/syntax_error.rb +4 -0
  100. data/lib/nokogiri/version.rb +88 -0
  101. data/lib/nokogiri/xml.rb +67 -0
  102. data/lib/nokogiri/xml/attr.rb +14 -0
  103. data/lib/nokogiri/xml/attribute_decl.rb +18 -0
  104. data/lib/nokogiri/xml/builder.rb +426 -0
  105. data/lib/nokogiri/xml/cdata.rb +11 -0
  106. data/lib/nokogiri/xml/character_data.rb +7 -0
  107. data/lib/nokogiri/xml/document.rb +234 -0
  108. data/lib/nokogiri/xml/document_fragment.rb +98 -0
  109. data/lib/nokogiri/xml/dtd.rb +22 -0
  110. data/lib/nokogiri/xml/element_content.rb +36 -0
  111. data/lib/nokogiri/xml/element_decl.rb +13 -0
  112. data/lib/nokogiri/xml/entity_decl.rb +19 -0
  113. data/lib/nokogiri/xml/namespace.rb +13 -0
  114. data/lib/nokogiri/xml/node.rb +915 -0
  115. data/lib/nokogiri/xml/node/save_options.rb +61 -0
  116. data/lib/nokogiri/xml/node_set.rb +357 -0
  117. data/lib/nokogiri/xml/notation.rb +6 -0
  118. data/lib/nokogiri/xml/parse_options.rb +93 -0
  119. data/lib/nokogiri/xml/pp.rb +2 -0
  120. data/lib/nokogiri/xml/pp/character_data.rb +18 -0
  121. data/lib/nokogiri/xml/pp/node.rb +56 -0
  122. data/lib/nokogiri/xml/processing_instruction.rb +8 -0
  123. data/lib/nokogiri/xml/reader.rb +112 -0
  124. data/lib/nokogiri/xml/relax_ng.rb +32 -0
  125. data/lib/nokogiri/xml/sax.rb +4 -0
  126. data/lib/nokogiri/xml/sax/document.rb +164 -0
  127. data/lib/nokogiri/xml/sax/parser.rb +115 -0
  128. data/lib/nokogiri/xml/sax/parser_context.rb +16 -0
  129. data/lib/nokogiri/xml/sax/push_parser.rb +60 -0
  130. data/lib/nokogiri/xml/schema.rb +63 -0
  131. data/lib/nokogiri/xml/syntax_error.rb +47 -0
  132. data/lib/nokogiri/xml/text.rb +9 -0
  133. data/lib/nokogiri/xml/xpath.rb +10 -0
  134. data/lib/nokogiri/xml/xpath/syntax_error.rb +11 -0
  135. data/lib/nokogiri/xml/xpath_context.rb +16 -0
  136. data/lib/nokogiri/xslt.rb +52 -0
  137. data/lib/nokogiri/xslt/stylesheet.rb +25 -0
  138. data/lib/xsd/xmlparser/nokogiri.rb +90 -0
  139. data/nokogiri_help_responses.md +40 -0
  140. data/tasks/cross_compile.rb +152 -0
  141. data/tasks/nokogiri.org.rb +18 -0
  142. data/tasks/test.rb +94 -0
  143. data/test/css/test_nthiness.rb +159 -0
  144. data/test/css/test_parser.rb +303 -0
  145. data/test/css/test_tokenizer.rb +198 -0
  146. data/test/css/test_xpath_visitor.rb +85 -0
  147. data/test/decorators/test_slop.rb +16 -0
  148. data/test/files/2ch.html +108 -0
  149. data/test/files/address_book.rlx +12 -0
  150. data/test/files/address_book.xml +10 -0
  151. data/test/files/bar/bar.xsd +4 -0
  152. data/test/files/dont_hurt_em_why.xml +422 -0
  153. data/test/files/encoding.html +82 -0
  154. data/test/files/encoding.xhtml +84 -0
  155. data/test/files/exslt.xml +8 -0
  156. data/test/files/exslt.xslt +35 -0
  157. data/test/files/foo/foo.xsd +4 -0
  158. data/test/files/metacharset.html +10 -0
  159. data/test/files/noencoding.html +47 -0
  160. data/test/files/po.xml +32 -0
  161. data/test/files/po.xsd +66 -0
  162. data/test/files/shift_jis.html +10 -0
  163. data/test/files/shift_jis.xml +5 -0
  164. data/test/files/snuggles.xml +3 -0
  165. data/test/files/staff.dtd +10 -0
  166. data/test/files/staff.xml +59 -0
  167. data/test/files/staff.xslt +32 -0
  168. data/test/files/tlm.html +850 -0
  169. data/test/files/valid_bar.xml +2 -0
  170. data/test/helper.rb +173 -0
  171. data/test/html/sax/test_parser.rb +139 -0
  172. data/test/html/sax/test_parser_context.rb +48 -0
  173. data/test/html/test_builder.rb +165 -0
  174. data/test/html/test_document.rb +472 -0
  175. data/test/html/test_document_encoding.rb +138 -0
  176. data/test/html/test_document_fragment.rb +255 -0
  177. data/test/html/test_element_description.rb +101 -0
  178. data/test/html/test_named_characters.rb +14 -0
  179. data/test/html/test_node.rb +193 -0
  180. data/test/html/test_node_encoding.rb +27 -0
  181. data/test/test_convert_xpath.rb +135 -0
  182. data/test/test_css_cache.rb +45 -0
  183. data/test/test_encoding_handler.rb +46 -0
  184. data/test/test_memory_leak.rb +72 -0
  185. data/test/test_nokogiri.rb +133 -0
  186. data/test/test_reader.rb +425 -0
  187. data/test/test_soap4r_sax.rb +52 -0
  188. data/test/test_xslt_transforms.rb +193 -0
  189. data/test/xml/node/test_save_options.rb +28 -0
  190. data/test/xml/node/test_subclass.rb +44 -0
  191. data/test/xml/sax/test_parser.rb +338 -0
  192. data/test/xml/sax/test_parser_context.rb +113 -0
  193. data/test/xml/sax/test_push_parser.rb +156 -0
  194. data/test/xml/test_attr.rb +65 -0
  195. data/test/xml/test_attribute_decl.rb +86 -0
  196. data/test/xml/test_builder.rb +227 -0
  197. data/test/xml/test_cdata.rb +50 -0
  198. data/test/xml/test_comment.rb +29 -0
  199. data/test/xml/test_document.rb +697 -0
  200. data/test/xml/test_document_encoding.rb +26 -0
  201. data/test/xml/test_document_fragment.rb +192 -0
  202. data/test/xml/test_dtd.rb +107 -0
  203. data/test/xml/test_dtd_encoding.rb +33 -0
  204. data/test/xml/test_element_content.rb +56 -0
  205. data/test/xml/test_element_decl.rb +73 -0
  206. data/test/xml/test_entity_decl.rb +122 -0
  207. data/test/xml/test_entity_reference.rb +21 -0
  208. data/test/xml/test_namespace.rb +70 -0
  209. data/test/xml/test_node.rb +917 -0
  210. data/test/xml/test_node_attributes.rb +34 -0
  211. data/test/xml/test_node_encoding.rb +107 -0
  212. data/test/xml/test_node_reparenting.rb +334 -0
  213. data/test/xml/test_node_set.rb +742 -0
  214. data/test/xml/test_parse_options.rb +52 -0
  215. data/test/xml/test_processing_instruction.rb +30 -0
  216. data/test/xml/test_reader_encoding.rb +126 -0
  217. data/test/xml/test_relax_ng.rb +60 -0
  218. data/test/xml/test_schema.rb +94 -0
  219. data/test/xml/test_syntax_error.rb +12 -0
  220. data/test/xml/test_text.rb +47 -0
  221. data/test/xml/test_unparented_node.rb +381 -0
  222. data/test/xml/test_xpath.rb +237 -0
  223. data/test/xslt/test_custom_functions.rb +94 -0
  224. data/test/xslt/test_exception_handling.rb +37 -0
  225. metadata +548 -0
@@ -0,0 +1,12 @@
1
+ #ifndef HAVE_XMLFIRSTELEMENTCHILD
2
+
3
+ #ifndef XML_LIBXML2_HACKS
4
+ #define XML_LIBXML2_HACKS
5
+
6
+ xmlNodePtr xmlFirstElementChild(xmlNodePtr parent);
7
+ xmlNodePtr xmlNextElementSibling(xmlNodePtr node);
8
+ xmlNodePtr xmlLastElementChild(xmlNodePtr parent);
9
+
10
+ #endif
11
+
12
+ #endif
@@ -0,0 +1,84 @@
1
+ #include <xml_namespace.h>
2
+
3
+ VALUE cNokogiriXmlNamespace ;
4
+
5
+ /*
6
+ * call-seq:
7
+ * prefix
8
+ *
9
+ * Get the prefix for this namespace. Returns +nil+ if there is no prefix.
10
+ */
11
+ static VALUE prefix(VALUE self)
12
+ {
13
+ xmlNsPtr ns;
14
+ xmlDocPtr doc;
15
+
16
+ Data_Get_Struct(self, xmlNs, ns);
17
+ if(!ns->prefix) return Qnil;
18
+
19
+ Data_Get_Struct(rb_iv_get(self, "@document"), xmlDoc, doc);
20
+
21
+ return NOKOGIRI_STR_NEW2(ns->prefix);
22
+ }
23
+
24
+ /*
25
+ * call-seq:
26
+ * href
27
+ *
28
+ * Get the href for this namespace
29
+ */
30
+ static VALUE href(VALUE self)
31
+ {
32
+ xmlNsPtr ns;
33
+ xmlDocPtr doc;
34
+
35
+ Data_Get_Struct(self, xmlNs, ns);
36
+ if(!ns->href) return Qnil;
37
+
38
+ Data_Get_Struct(rb_iv_get(self, "@document"), xmlDoc, doc);
39
+
40
+ return NOKOGIRI_STR_NEW2(ns->href);
41
+ }
42
+
43
+ VALUE Nokogiri_wrap_xml_namespace(xmlDocPtr doc, xmlNsPtr node)
44
+ {
45
+ VALUE ns, document, node_cache;
46
+
47
+ assert(doc->_private);
48
+
49
+ if(node->_private)
50
+ return (VALUE)node->_private;
51
+
52
+ ns = Data_Wrap_Struct(cNokogiriXmlNamespace, 0, 0, node);
53
+
54
+ document = DOC_RUBY_OBJECT(doc);
55
+
56
+ node_cache = rb_iv_get(document, "@node_cache");
57
+ rb_ary_push(node_cache, ns);
58
+
59
+ rb_iv_set(ns, "@document", DOC_RUBY_OBJECT(doc));
60
+
61
+ node->_private = (void *)ns;
62
+
63
+ return ns;
64
+ }
65
+
66
+ VALUE Nokogiri_wrap_xml_namespace2(VALUE document, xmlNsPtr node)
67
+ {
68
+ xmlDocPtr doc;
69
+ Data_Get_Struct(document, xmlDoc, doc) ;
70
+ return Nokogiri_wrap_xml_namespace(doc, node);
71
+ }
72
+
73
+
74
+ void init_xml_namespace()
75
+ {
76
+ VALUE nokogiri = rb_define_module("Nokogiri");
77
+ VALUE xml = rb_define_module_under(nokogiri, "XML");
78
+ VALUE klass = rb_define_class_under(xml, "Namespace", rb_cObject);
79
+
80
+ cNokogiriXmlNamespace = klass;
81
+
82
+ rb_define_method(klass, "prefix", prefix, 0);
83
+ rb_define_method(klass, "href", href, 0);
84
+ }
@@ -0,0 +1,13 @@
1
+ #ifndef NOKOGIRI_XML_NAMESPACE
2
+ #define NOKOGIRI_XML_NAMESPACE
3
+
4
+ #include <nokogiri.h>
5
+
6
+ void init_xml_namespace();
7
+
8
+ extern VALUE cNokogiriXmlNamespace ;
9
+
10
+ VALUE Nokogiri_wrap_xml_namespace(xmlDocPtr doc, xmlNsPtr node) ;
11
+ VALUE Nokogiri_wrap_xml_namespace2(VALUE document, xmlNsPtr node) ;
12
+
13
+ #endif
@@ -0,0 +1,1397 @@
1
+ #include <xml_node.h>
2
+
3
+ static ID decorate, decorate_bang;
4
+
5
+ #ifdef DEBUG
6
+ static void debug_node_dealloc(xmlNodePtr x)
7
+ {
8
+ NOKOGIRI_DEBUG_START(x)
9
+ NOKOGIRI_DEBUG_END(x)
10
+ }
11
+ #else
12
+ # define debug_node_dealloc 0
13
+ #endif
14
+
15
+ static void mark(xmlNodePtr node)
16
+ {
17
+ // rb_gc_mark(DOC_RUBY_OBJECT(node->doc));
18
+ }
19
+
20
+ /* :nodoc: */
21
+ typedef xmlNodePtr (*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
22
+
23
+ /* :nodoc: */
24
+ static void relink_namespace(xmlNodePtr reparented)
25
+ {
26
+ xmlNodePtr child;
27
+
28
+ /* Avoid segv when relinking against unlinked nodes. */
29
+ if(!reparented->parent) return;
30
+
31
+ /* Make sure that our reparented node has the correct namespaces */
32
+ if(!reparented->ns && reparented->doc != (xmlDocPtr)reparented->parent)
33
+ xmlSetNs(reparented, reparented->parent->ns);
34
+
35
+ /* Search our parents for an existing definition */
36
+ if(reparented->nsDef) {
37
+ xmlNsPtr curr = reparented->nsDef;
38
+ xmlNsPtr prev = NULL;
39
+
40
+ while(curr) {
41
+ xmlNsPtr ns = xmlSearchNsByHref(
42
+ reparented->doc,
43
+ reparented->parent,
44
+ curr->href
45
+ );
46
+ /* If we find the namespace is already declared, remove it from this
47
+ * definition list. */
48
+ if(ns && ns != curr) {
49
+ if (prev) {
50
+ prev->next = curr->next;
51
+ } else {
52
+ reparented->nsDef = curr->next;
53
+ }
54
+ NOKOGIRI_ROOT_NSDEF(curr, reparented->doc);
55
+ } else {
56
+ prev = curr;
57
+ }
58
+ curr = curr->next;
59
+ }
60
+ }
61
+
62
+ /* Only walk all children if there actually is a namespace we need to */
63
+ /* reparent. */
64
+ if(NULL == reparented->ns) return;
65
+
66
+ /* When a node gets reparented, walk it's children to make sure that */
67
+ /* their namespaces are reparented as well. */
68
+ child = reparented->children;
69
+ while(NULL != child) {
70
+ relink_namespace(child);
71
+ child = child->next;
72
+ }
73
+ }
74
+
75
+ /* :nodoc: */
76
+ static xmlNodePtr xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
77
+ {
78
+ xmlNodePtr retval ;
79
+
80
+ retval = xmlReplaceNode(pivot, new_node) ;
81
+
82
+ if (retval == pivot) {
83
+ retval = new_node ; /* return semantics for reparent_node_with */
84
+ }
85
+
86
+ /* work around libxml2 issue: https://bugzilla.gnome.org/show_bug.cgi?id=615612 */
87
+ if (retval->type == XML_TEXT_NODE) {
88
+ if (retval->prev && retval->prev->type == XML_TEXT_NODE) {
89
+ retval = xmlTextMerge(retval->prev, retval);
90
+ }
91
+ if (retval->next && retval->next->type == XML_TEXT_NODE) {
92
+ retval = xmlTextMerge(retval, retval->next);
93
+ }
94
+ }
95
+
96
+ return retval ;
97
+ }
98
+
99
+ /* :nodoc: */
100
+ static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
101
+ {
102
+ VALUE reparented_obj ;
103
+ xmlNodePtr reparentee, pivot, reparented, next_text, new_next_text ;
104
+
105
+ if(! rb_obj_is_kind_of_(reparentee_obj, cNokogiriXmlNode))
106
+ rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
107
+ if( rb_obj_is_kind_of_(reparentee_obj, cNokogiriXmlDocument))
108
+ rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
109
+
110
+ Data_Get_Struct(reparentee_obj, xmlNode, reparentee);
111
+ Data_Get_Struct(pivot_obj, xmlNode, pivot);
112
+
113
+ if(XML_DOCUMENT_NODE == reparentee->type || XML_HTML_DOCUMENT_NODE == reparentee->type)
114
+ rb_raise(rb_eArgError, "cannot reparent a document node");
115
+
116
+ xmlUnlinkNode(reparentee);
117
+
118
+ if (reparentee->doc != pivot->doc || reparentee->type == XML_TEXT_NODE) {
119
+ /*
120
+ * if the reparentee is a text node, there's a very good chance it will be
121
+ * merged with an adjacent text node after being reparented, and in that case
122
+ * libxml will free the underlying C struct.
123
+ *
124
+ * since we clearly have a ruby object which references the underlying
125
+ * memory, we can't let the C struct get freed. let's pickle the original
126
+ * reparentee by rooting it; and then we'll reparent a duplicate of the
127
+ * node that we don't care about preserving.
128
+ *
129
+ * alternatively, if the reparentee is from a different document than the
130
+ * pivot node, libxml2 is going to get confused about which document's
131
+ * "dictionary" the node's strings belong to (this is an otherwise
132
+ * uninteresting libxml2 implementation detail). as a result, we cannot
133
+ * reparent the actual reparentee, so we reparent a duplicate.
134
+ */
135
+ NOKOGIRI_ROOT_NODE(reparentee);
136
+ if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
137
+ rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
138
+ }
139
+ }
140
+
141
+ if (reparentee->type == XML_TEXT_NODE && pivot->next && pivot->next->type == XML_TEXT_NODE) {
142
+ /*
143
+ * libxml merges text nodes in a right-to-left fashion, meaning that if
144
+ * there are two text nodes who would be adjacent, the right (or following,
145
+ * or next) node will be merged into the left (or preceding, or previous)
146
+ * node.
147
+ *
148
+ * and by "merged" I mean the string contents will be concatenated onto the
149
+ * left node's contents, and then the node will be freed.
150
+ *
151
+ * which means that if we have a ruby object wrapped around the right node,
152
+ * its memory would be freed out from under it.
153
+ *
154
+ * so, we detect this edge case and unlink-and-root the text node before it gets
155
+ * merged. then we dup the node and insert that duplicate back into the
156
+ * document where the real node was.
157
+ *
158
+ * yes, this is totally lame.
159
+ */
160
+ next_text = pivot->next ;
161
+ new_next_text = xmlDocCopyNode(next_text, pivot->doc, 1) ;
162
+
163
+ xmlUnlinkNode(next_text);
164
+ NOKOGIRI_ROOT_NODE(next_text);
165
+
166
+ xmlAddNextSibling(pivot, new_next_text);
167
+ }
168
+
169
+ if(!(reparented = (*prf)(pivot, reparentee))) {
170
+ rb_raise(rb_eRuntimeError, "Could not reparent node");
171
+ }
172
+
173
+ /*
174
+ * make sure the ruby object is pointed at the just-reparented node, which
175
+ * might be a duplicate (see above) or might be the result of merging
176
+ * adjacent text nodes.
177
+ */
178
+ rb_rdata_store(reparentee_obj, reparented); // DATA_PTR(reparentee_obj) = reparented ;
179
+
180
+ relink_namespace(reparented);
181
+
182
+ reparented_obj = Nokogiri_wrap_xml_node(Qnil, reparented);
183
+
184
+ rb_funcall(reparented_obj, decorate_bang, 0);
185
+
186
+ return reparented_obj ;
187
+ }
188
+
189
+
190
+ /*
191
+ * call-seq:
192
+ * document
193
+ *
194
+ * Get the document for this Node
195
+ */
196
+ static VALUE document(VALUE self)
197
+ {
198
+ xmlNodePtr node;
199
+ Data_Get_Struct(self, xmlNode, node);
200
+ return DOC_RUBY_OBJECT(node->doc);
201
+ }
202
+
203
+ /*
204
+ * call-seq:
205
+ * pointer_id
206
+ *
207
+ * Get the internal pointer number
208
+ */
209
+ static VALUE pointer_id(VALUE self)
210
+ {
211
+ xmlNodePtr node;
212
+ Data_Get_Struct(self, xmlNode, node);
213
+
214
+ return INT2NUM((long)(node));
215
+ }
216
+
217
+ /*
218
+ * call-seq:
219
+ * encode_special_chars(string)
220
+ *
221
+ * Encode any special characters in +string+
222
+ */
223
+ static VALUE encode_special_chars(VALUE self, VALUE string)
224
+ {
225
+ xmlNodePtr node;
226
+ xmlChar *encoded;
227
+ VALUE encoded_str;
228
+
229
+ Data_Get_Struct(self, xmlNode, node);
230
+ encoded = xmlEncodeSpecialChars(
231
+ node->doc,
232
+ (const xmlChar *)StringValuePtr(string)
233
+ );
234
+
235
+ encoded_str = NOKOGIRI_STR_NEW2(encoded);
236
+ xmlFree(encoded);
237
+
238
+ return encoded_str;
239
+ }
240
+
241
+ /*
242
+ * call-seq:
243
+ * create_internal_subset(name, external_id, system_id)
244
+ *
245
+ * Create the internal subset of a document.
246
+ *
247
+ * doc.create_internal_subset("chapter", "-//OASIS//DTD DocBook XML//EN", "chapter.dtd")
248
+ * # => <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML//EN" "chapter.dtd">
249
+ *
250
+ * doc.create_internal_subset("chapter", nil, "chapter.dtd")
251
+ * # => <!DOCTYPE chapter SYSTEM "chapter.dtd">
252
+ */
253
+ static VALUE create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
254
+ {
255
+ xmlNodePtr node;
256
+ xmlDocPtr doc;
257
+ xmlDtdPtr dtd;
258
+
259
+ Data_Get_Struct(self, xmlNode, node);
260
+
261
+ doc = node->doc;
262
+
263
+ if(xmlGetIntSubset(doc))
264
+ rb_raise(rb_eRuntimeError, "Document already has an internal subset");
265
+
266
+ dtd = xmlCreateIntSubset(
267
+ doc,
268
+ NIL_P(name) ? NULL : (const xmlChar *)StringValuePtr(name),
269
+ NIL_P(external_id) ? NULL : (const xmlChar *)StringValuePtr(external_id),
270
+ NIL_P(system_id) ? NULL : (const xmlChar *)StringValuePtr(system_id)
271
+ );
272
+
273
+ if(!dtd) return Qnil;
274
+
275
+ return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
276
+ }
277
+
278
+ /*
279
+ * call-seq:
280
+ * create_external_subset(name, external_id, system_id)
281
+ *
282
+ * Create an external subset
283
+ */
284
+ static VALUE create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
285
+ {
286
+ xmlNodePtr node;
287
+ xmlDocPtr doc;
288
+ xmlDtdPtr dtd;
289
+
290
+ Data_Get_Struct(self, xmlNode, node);
291
+
292
+ doc = node->doc;
293
+
294
+ if(doc->extSubset)
295
+ rb_raise(rb_eRuntimeError, "Document already has an external subset");
296
+
297
+ dtd = xmlNewDtd(
298
+ doc,
299
+ NIL_P(name) ? NULL : (const xmlChar *)StringValuePtr(name),
300
+ NIL_P(external_id) ? NULL : (const xmlChar *)StringValuePtr(external_id),
301
+ NIL_P(system_id) ? NULL : (const xmlChar *)StringValuePtr(system_id)
302
+ );
303
+
304
+ if(!dtd) return Qnil;
305
+
306
+ return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
307
+ }
308
+
309
+ /*
310
+ * call-seq:
311
+ * external_subset
312
+ *
313
+ * Get the external subset
314
+ */
315
+ static VALUE external_subset(VALUE self)
316
+ {
317
+ xmlNodePtr node;
318
+ xmlDocPtr doc;
319
+ xmlDtdPtr dtd;
320
+
321
+ Data_Get_Struct(self, xmlNode, node);
322
+
323
+ if(!node->doc) return Qnil;
324
+
325
+ doc = node->doc;
326
+ dtd = doc->extSubset;
327
+
328
+ if(!dtd) return Qnil;
329
+
330
+ return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
331
+ }
332
+
333
+ /*
334
+ * call-seq:
335
+ * internal_subset
336
+ *
337
+ * Get the internal subset
338
+ */
339
+ static VALUE internal_subset(VALUE self)
340
+ {
341
+ xmlNodePtr node;
342
+ xmlDocPtr doc;
343
+ xmlDtdPtr dtd;
344
+
345
+ Data_Get_Struct(self, xmlNode, node);
346
+
347
+ if(!node->doc) return Qnil;
348
+
349
+ doc = node->doc;
350
+ dtd = xmlGetIntSubset(doc);
351
+
352
+ if(!dtd) return Qnil;
353
+
354
+ return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)dtd);
355
+ }
356
+
357
+ /*
358
+ * call-seq:
359
+ * dup
360
+ *
361
+ * Copy this node. An optional depth may be passed in, but it defaults
362
+ * to a deep copy. 0 is a shallow copy, 1 is a deep copy.
363
+ */
364
+ static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
365
+ {
366
+ VALUE level;
367
+ xmlNodePtr node, dup;
368
+
369
+ if(rb_scan_args(argc, argv, "01", &level) == 0)
370
+ level = INT2NUM((long)1);
371
+
372
+ Data_Get_Struct(self, xmlNode, node);
373
+
374
+ dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
375
+ if(dup == NULL) return Qnil;
376
+
377
+ NOKOGIRI_ROOT_NODE(dup);
378
+
379
+ return Nokogiri_wrap_xml_node(rb_obj_class(self), dup);
380
+ }
381
+
382
+ /*
383
+ * call-seq:
384
+ * unlink
385
+ *
386
+ * Unlink this node from its current context.
387
+ */
388
+ static VALUE unlink_node(VALUE self)
389
+ {
390
+ xmlNodePtr node;
391
+ Data_Get_Struct(self, xmlNode, node);
392
+ xmlUnlinkNode(node);
393
+ NOKOGIRI_ROOT_NODE(node);
394
+ return self;
395
+ }
396
+
397
+ /*
398
+ * call-seq:
399
+ * blank?
400
+ *
401
+ * Is this node blank?
402
+ */
403
+ static VALUE blank_eh(VALUE self)
404
+ {
405
+ xmlNodePtr node;
406
+ Data_Get_Struct(self, xmlNode, node);
407
+ return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
408
+ }
409
+
410
+ /*
411
+ * call-seq:
412
+ * next_sibling
413
+ *
414
+ * Returns the next sibling node
415
+ */
416
+ static VALUE next_sibling(VALUE self)
417
+ {
418
+ xmlNodePtr node, sibling;
419
+ Data_Get_Struct(self, xmlNode, node);
420
+
421
+ sibling = node->next;
422
+ if(!sibling) return Qnil;
423
+
424
+ return Nokogiri_wrap_xml_node(Qnil, sibling) ;
425
+ }
426
+
427
+ /*
428
+ * call-seq:
429
+ * previous_sibling
430
+ *
431
+ * Returns the previous sibling node
432
+ */
433
+ static VALUE previous_sibling(VALUE self)
434
+ {
435
+ xmlNodePtr node, sibling;
436
+ Data_Get_Struct(self, xmlNode, node);
437
+
438
+ sibling = node->prev;
439
+ if(!sibling) return Qnil;
440
+
441
+ return Nokogiri_wrap_xml_node(Qnil, sibling);
442
+ }
443
+
444
+ /*
445
+ * call-seq:
446
+ * next_element
447
+ *
448
+ * Returns the next Nokogiri::XML::Element type sibling node.
449
+ */
450
+ static VALUE next_element(VALUE self)
451
+ {
452
+ xmlNodePtr node, sibling;
453
+ Data_Get_Struct(self, xmlNode, node);
454
+
455
+ sibling = xmlNextElementSibling(node);
456
+ if(!sibling) return Qnil;
457
+
458
+ return Nokogiri_wrap_xml_node(Qnil, sibling);
459
+ }
460
+
461
+ /*
462
+ * call-seq:
463
+ * previous_element
464
+ *
465
+ * Returns the previous Nokogiri::XML::Element type sibling node.
466
+ */
467
+ static VALUE previous_element(VALUE self)
468
+ {
469
+ xmlNodePtr node, sibling;
470
+ Data_Get_Struct(self, xmlNode, node);
471
+
472
+ /*
473
+ * note that we don't use xmlPreviousElementSibling here because it's buggy pre-2.7.7.
474
+ */
475
+ sibling = node->prev;
476
+ if(!sibling) return Qnil;
477
+
478
+ while(sibling && sibling->type != XML_ELEMENT_NODE)
479
+ sibling = sibling->prev;
480
+
481
+ return sibling ? Nokogiri_wrap_xml_node(Qnil, sibling) : Qnil ;
482
+ }
483
+
484
+ /* :nodoc: */
485
+ static VALUE replace(VALUE self, VALUE new_node)
486
+ {
487
+ return reparent_node_with(self, new_node, xmlReplaceNodeWrapper) ;
488
+ }
489
+
490
+ /*
491
+ * call-seq:
492
+ * children
493
+ *
494
+ * Get the list of children for this node as a NodeSet
495
+ */
496
+ static VALUE children(VALUE self)
497
+ {
498
+ xmlNodePtr node;
499
+ xmlNodePtr child;
500
+ xmlNodeSetPtr set;
501
+ VALUE document;
502
+ VALUE node_set;
503
+
504
+ Data_Get_Struct(self, xmlNode, node);
505
+
506
+ child = node->children;
507
+ set = xmlXPathNodeSetCreate(child);
508
+
509
+ document = DOC_RUBY_OBJECT(node->doc);
510
+
511
+ if(!child) return Nokogiri_wrap_xml_node_set(set, document);
512
+
513
+ child = child->next;
514
+ while(NULL != child) {
515
+ xmlXPathNodeSetAddUnique(set, child);
516
+ child = child->next;
517
+ }
518
+
519
+ node_set = Nokogiri_wrap_xml_node_set(set, document);
520
+
521
+ return node_set;
522
+ }
523
+
524
+ /*
525
+ * call-seq:
526
+ * element_children
527
+ *
528
+ * Get the list of children for this node as a NodeSet. All nodes will be
529
+ * element nodes.
530
+ *
531
+ * Example:
532
+ *
533
+ * @doc.root.element_children.all? { |x| x.element? } # => true
534
+ */
535
+ static VALUE element_children(VALUE self)
536
+ {
537
+ xmlNodePtr node;
538
+ xmlNodePtr child;
539
+ xmlNodeSetPtr set;
540
+ VALUE document;
541
+ VALUE node_set;
542
+
543
+ Data_Get_Struct(self, xmlNode, node);
544
+
545
+ child = xmlFirstElementChild(node);
546
+ set = xmlXPathNodeSetCreate(child);
547
+
548
+ document = DOC_RUBY_OBJECT(node->doc);
549
+
550
+ if(!child) return Nokogiri_wrap_xml_node_set(set, document);
551
+
552
+ child = xmlNextElementSibling(child);
553
+ while(NULL != child) {
554
+ xmlXPathNodeSetAddUnique(set, child);
555
+ child = xmlNextElementSibling(child);
556
+ }
557
+
558
+ node_set = Nokogiri_wrap_xml_node_set(set, document);
559
+
560
+ return node_set;
561
+ }
562
+
563
+ /*
564
+ * call-seq:
565
+ * child
566
+ *
567
+ * Returns the child node
568
+ */
569
+ static VALUE child(VALUE self)
570
+ {
571
+ xmlNodePtr node, child;
572
+ Data_Get_Struct(self, xmlNode, node);
573
+
574
+ child = node->children;
575
+ if(!child) return Qnil;
576
+
577
+ return Nokogiri_wrap_xml_node(Qnil, child);
578
+ }
579
+
580
+ /*
581
+ * call-seq:
582
+ * first_element_child
583
+ *
584
+ * Returns the first child node of this node that is an element.
585
+ *
586
+ * Example:
587
+ *
588
+ * @doc.root.first_element_child.element? # => true
589
+ */
590
+ static VALUE first_element_child(VALUE self)
591
+ {
592
+ xmlNodePtr node, child;
593
+ Data_Get_Struct(self, xmlNode, node);
594
+
595
+ child = xmlFirstElementChild(node);
596
+ if(!child) return Qnil;
597
+
598
+ return Nokogiri_wrap_xml_node(Qnil, child);
599
+ }
600
+
601
+ /*
602
+ * call-seq:
603
+ * last_element_child
604
+ *
605
+ * Returns the last child node of this node that is an element.
606
+ *
607
+ * Example:
608
+ *
609
+ * @doc.root.last_element_child.element? # => true
610
+ */
611
+ static VALUE last_element_child(VALUE self)
612
+ {
613
+ xmlNodePtr node, child;
614
+ Data_Get_Struct(self, xmlNode, node);
615
+
616
+ child = xmlLastElementChild(node);
617
+ if(!child) return Qnil;
618
+
619
+ return Nokogiri_wrap_xml_node(Qnil, child);
620
+ }
621
+
622
+ /*
623
+ * call-seq:
624
+ * key?(attribute)
625
+ *
626
+ * Returns true if +attribute+ is set
627
+ */
628
+ static VALUE key_eh(VALUE self, VALUE attribute)
629
+ {
630
+ xmlNodePtr node;
631
+ Data_Get_Struct(self, xmlNode, node);
632
+ if(xmlHasProp(node, (xmlChar *)StringValuePtr(attribute)))
633
+ return Qtrue;
634
+ return Qfalse;
635
+ }
636
+
637
+ /*
638
+ * call-seq:
639
+ * namespaced_key?(attribute, namespace)
640
+ *
641
+ * Returns true if +attribute+ is set with +namespace+
642
+ */
643
+ static VALUE namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
644
+ {
645
+ xmlNodePtr node;
646
+ Data_Get_Struct(self, xmlNode, node);
647
+ if(xmlHasNsProp(node, (xmlChar *)StringValuePtr(attribute),
648
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValuePtr(namespace)))
649
+ return Qtrue;
650
+ return Qfalse;
651
+ }
652
+
653
+ /*
654
+ * call-seq:
655
+ * []=(property, value)
656
+ *
657
+ * Set the +property+ to +value+
658
+ */
659
+ static VALUE set(VALUE self, VALUE property, VALUE value)
660
+ {
661
+ xmlNodePtr node;
662
+ Data_Get_Struct(self, xmlNode, node);
663
+
664
+ xmlSetProp(node, (xmlChar *)StringValuePtr(property),
665
+ (xmlChar *)StringValuePtr(value));
666
+
667
+ return value;
668
+ }
669
+
670
+ /*
671
+ * call-seq:
672
+ * get(attribute)
673
+ *
674
+ * Get the value for +attribute+
675
+ */
676
+ static VALUE get(VALUE self, VALUE attribute)
677
+ {
678
+ xmlNodePtr node;
679
+ xmlChar* propstr ;
680
+ VALUE rval ;
681
+ Data_Get_Struct(self, xmlNode, node);
682
+
683
+ if(NIL_P(attribute)) return Qnil;
684
+
685
+ propstr = xmlGetProp(node, (xmlChar *)StringValuePtr(attribute));
686
+
687
+ if(!propstr) return Qnil;
688
+
689
+ rval = NOKOGIRI_STR_NEW2(propstr);
690
+
691
+ xmlFree(propstr);
692
+ return rval ;
693
+ }
694
+
695
+ /*
696
+ * call-seq:
697
+ * set_namespace(namespace)
698
+ *
699
+ * Set the namespace to +namespace+
700
+ */
701
+ static VALUE set_namespace(VALUE self, VALUE namespace)
702
+ {
703
+ xmlNodePtr node;
704
+ xmlNsPtr ns = NULL;
705
+
706
+ Data_Get_Struct(self, xmlNode, node);
707
+
708
+ if(!NIL_P(namespace))
709
+ Data_Get_Struct(namespace, xmlNs, ns);
710
+
711
+ xmlSetNs(node, ns);
712
+
713
+ return self;
714
+ }
715
+
716
+ /*
717
+ * call-seq:
718
+ * attribute(name)
719
+ *
720
+ * Get the attribute node with +name+
721
+ */
722
+ static VALUE attr(VALUE self, VALUE name)
723
+ {
724
+ xmlNodePtr node;
725
+ xmlAttrPtr prop;
726
+ Data_Get_Struct(self, xmlNode, node);
727
+ prop = xmlHasProp(node, (xmlChar *)StringValuePtr(name));
728
+
729
+ if(! prop) return Qnil;
730
+ return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
731
+ }
732
+
733
+ /*
734
+ * call-seq:
735
+ * attribute_with_ns(name, namespace)
736
+ *
737
+ * Get the attribute node with +name+ and +namespace+
738
+ */
739
+ static VALUE attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
740
+ {
741
+ xmlNodePtr node;
742
+ xmlAttrPtr prop;
743
+ Data_Get_Struct(self, xmlNode, node);
744
+ prop = xmlHasNsProp(node, (xmlChar *)StringValuePtr(name),
745
+ NIL_P(namespace) ? NULL : (xmlChar *)StringValuePtr(namespace));
746
+
747
+ if(! prop) return Qnil;
748
+ return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
749
+ }
750
+
751
+ /*
752
+ * call-seq:
753
+ * attribute_nodes()
754
+ *
755
+ * returns a list containing the Node attributes.
756
+ */
757
+ static VALUE attribute_nodes(VALUE self)
758
+ {
759
+ /* this code in the mode of xmlHasProp() */
760
+ xmlNodePtr node;
761
+ VALUE attr;
762
+
763
+ Data_Get_Struct(self, xmlNode, node);
764
+
765
+ attr = rb_ary_new();
766
+ Nokogiri_xml_node_properties(node, attr);
767
+
768
+ return attr ;
769
+ }
770
+
771
+
772
+ /*
773
+ * call-seq:
774
+ * namespace()
775
+ *
776
+ * returns the default namespace set on this node (as with an "xmlns="
777
+ * attribute), as a Namespace object.
778
+ */
779
+ static VALUE namespace(VALUE self)
780
+ {
781
+ xmlNodePtr node ;
782
+ Data_Get_Struct(self, xmlNode, node);
783
+
784
+ if (node->ns)
785
+ return Nokogiri_wrap_xml_namespace(node->doc, node->ns);
786
+
787
+ return Qnil ;
788
+ }
789
+
790
+ /*
791
+ * call-seq:
792
+ * namespace_definitions()
793
+ *
794
+ * 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=").
795
+ */
796
+ static VALUE namespace_definitions(VALUE self)
797
+ {
798
+ /* this code in the mode of xmlHasProp() */
799
+ xmlNodePtr node ;
800
+ VALUE list;
801
+ xmlNsPtr ns;
802
+
803
+ Data_Get_Struct(self, xmlNode, node);
804
+
805
+ list = rb_ary_new();
806
+
807
+ ns = node->nsDef;
808
+
809
+ if(!ns) return list;
810
+
811
+ while(NULL != ns) {
812
+ rb_ary_push(list, Nokogiri_wrap_xml_namespace(node->doc, ns));
813
+ ns = ns->next;
814
+ }
815
+
816
+ return list;
817
+ }
818
+
819
+ /*
820
+ * call-seq:
821
+ * namespace_scopes()
822
+ *
823
+ * returns namespaces in scope for self -- those defined on self element
824
+ * directly or any ancestor node -- as an array of Namespace objects. Default
825
+ * namespaces ("xmlns=" style) for self are included in this array; Default
826
+ * namespaces for ancestors, however, are not. See also #namespaces
827
+ */
828
+ static VALUE namespace_scopes(VALUE self)
829
+ {
830
+ xmlNodePtr node ;
831
+ VALUE list;
832
+ xmlNsPtr *ns_list;
833
+ int j;
834
+
835
+ Data_Get_Struct(self, xmlNode, node);
836
+
837
+ list = rb_ary_new();
838
+ ns_list = xmlGetNsList(node->doc, node);
839
+
840
+ if(!ns_list) return list;
841
+
842
+ for (j = 0 ; ns_list[j] != NULL ; ++j) {
843
+ rb_ary_push(list, Nokogiri_wrap_xml_namespace(node->doc, ns_list[j]));
844
+ }
845
+
846
+ xmlFree(ns_list);
847
+ return list;
848
+ }
849
+
850
+ /*
851
+ * call-seq:
852
+ * node_type
853
+ *
854
+ * Get the type for this Node
855
+ */
856
+ static VALUE node_type(VALUE self)
857
+ {
858
+ xmlNodePtr node;
859
+ Data_Get_Struct(self, xmlNode, node);
860
+ return INT2NUM((long)node->type);
861
+ }
862
+
863
+ /*
864
+ * call-seq:
865
+ * content=
866
+ *
867
+ * Set the content for this Node
868
+ */
869
+ static VALUE set_content(VALUE self, VALUE content)
870
+ {
871
+ xmlNodePtr node, child, next ;
872
+ Data_Get_Struct(self, xmlNode, node);
873
+
874
+ child = node->children;
875
+ while (NULL != child) {
876
+ next = child->next ;
877
+ xmlUnlinkNode(child) ;
878
+ NOKOGIRI_ROOT_NODE(child) ;
879
+ child = next ;
880
+ }
881
+
882
+ xmlNodeSetContent(node, (xmlChar *)StringValuePtr(content));
883
+ return content;
884
+ }
885
+
886
+ /*
887
+ * call-seq:
888
+ * content
889
+ *
890
+ * Returns the content for this Node
891
+ */
892
+ static VALUE get_content(VALUE self)
893
+ {
894
+ xmlNodePtr node;
895
+ xmlChar * content;
896
+
897
+ Data_Get_Struct(self, xmlNode, node);
898
+
899
+ content = xmlNodeGetContent(node);
900
+ if(content) {
901
+ VALUE rval = NOKOGIRI_STR_NEW2(content);
902
+ xmlFree(content);
903
+ return rval;
904
+ }
905
+ return Qnil;
906
+ }
907
+
908
+ /* :nodoc: */
909
+ static VALUE add_child(VALUE self, VALUE new_child)
910
+ {
911
+ return reparent_node_with(self, new_child, xmlAddChild);
912
+ }
913
+
914
+ /*
915
+ * call-seq:
916
+ * parent
917
+ *
918
+ * Get the parent Node for this Node
919
+ */
920
+ static VALUE get_parent(VALUE self)
921
+ {
922
+ xmlNodePtr node, parent;
923
+ Data_Get_Struct(self, xmlNode, node);
924
+
925
+ parent = node->parent;
926
+ if(!parent) return Qnil;
927
+
928
+ return Nokogiri_wrap_xml_node(Qnil, parent) ;
929
+ }
930
+
931
+ /*
932
+ * call-seq:
933
+ * name=(new_name)
934
+ *
935
+ * Set the name for this Node
936
+ */
937
+ static VALUE set_name(VALUE self, VALUE new_name)
938
+ {
939
+ xmlNodePtr node;
940
+ Data_Get_Struct(self, xmlNode, node);
941
+ xmlNodeSetName(node, (xmlChar*)StringValuePtr(new_name));
942
+ return new_name;
943
+ }
944
+
945
+ /*
946
+ * call-seq:
947
+ * name
948
+ *
949
+ * Returns the name for this Node
950
+ */
951
+ static VALUE get_name(VALUE self)
952
+ {
953
+ xmlNodePtr node;
954
+ Data_Get_Struct(self, xmlNode, node);
955
+ if(node->name)
956
+ return NOKOGIRI_STR_NEW2(node->name);
957
+ return Qnil;
958
+ }
959
+
960
+ /*
961
+ * call-seq:
962
+ * path
963
+ *
964
+ * Returns the path associated with this Node
965
+ */
966
+ static VALUE path(VALUE self)
967
+ {
968
+ xmlNodePtr node;
969
+ xmlChar *path ;
970
+ VALUE rval;
971
+
972
+ Data_Get_Struct(self, xmlNode, node);
973
+
974
+ path = xmlGetNodePath(node);
975
+ rval = NOKOGIRI_STR_NEW2(path);
976
+ xmlFree(path);
977
+ return rval ;
978
+ }
979
+
980
+ /* :nodoc: */
981
+ static VALUE add_next_sibling(VALUE self, VALUE new_sibling)
982
+ {
983
+ return reparent_node_with(self, new_sibling, xmlAddNextSibling) ;
984
+ }
985
+
986
+ /* :nodoc: */
987
+ static VALUE add_previous_sibling(VALUE self, VALUE new_sibling)
988
+ {
989
+ return reparent_node_with(self, new_sibling, xmlAddPrevSibling) ;
990
+ }
991
+
992
+ /*
993
+ * call-seq:
994
+ * native_write_to(io, encoding, options)
995
+ *
996
+ * Write this Node to +io+ with +encoding+ and +options+
997
+ */
998
+ static VALUE native_write_to(
999
+ VALUE self,
1000
+ VALUE io,
1001
+ VALUE encoding,
1002
+ VALUE indent_string,
1003
+ VALUE options
1004
+ ) {
1005
+ xmlNodePtr node;
1006
+ const char * before_indent;
1007
+ xmlSaveCtxtPtr savectx;
1008
+
1009
+ Data_Get_Struct(self, xmlNode, node);
1010
+
1011
+ xmlIndentTreeOutput = 1;
1012
+
1013
+ before_indent = xmlTreeIndentString;
1014
+
1015
+ xmlTreeIndentString = StringValuePtr(indent_string);
1016
+
1017
+ savectx = xmlSaveToIO(
1018
+ (xmlOutputWriteCallback)io_write_callback,
1019
+ (xmlOutputCloseCallback)io_close_callback,
1020
+ (void *)io,
1021
+ RTEST(encoding) ? StringValuePtr(encoding) : NULL,
1022
+ (int)NUM2INT(options)
1023
+ );
1024
+
1025
+ xmlSaveTree(savectx, node);
1026
+ xmlSaveClose(savectx);
1027
+
1028
+ xmlTreeIndentString = before_indent;
1029
+ return io;
1030
+ }
1031
+
1032
+ /*
1033
+ * call-seq:
1034
+ * line
1035
+ *
1036
+ * Returns the line for this Node
1037
+ */
1038
+ static VALUE line(VALUE self)
1039
+ {
1040
+ xmlNodePtr node;
1041
+ Data_Get_Struct(self, xmlNode, node);
1042
+
1043
+ return INT2NUM(xmlGetLineNo(node));
1044
+ }
1045
+
1046
+ /*
1047
+ * call-seq:
1048
+ * add_namespace_definition(prefix, href)
1049
+ *
1050
+ * Adds a namespace definition with +prefix+ using +href+ value. The result is
1051
+ * as if parsed XML for this node had included an attribute
1052
+ * 'xmlns:prefix=value'. A default namespace for this node ("xmlns=") can be
1053
+ * added by passing 'nil' for prefix. Namespaces added this way will not
1054
+ * show up in #attributes, but they will be included as an xmlns attribute
1055
+ * when the node is serialized to XML.
1056
+ */
1057
+ static VALUE add_namespace_definition(VALUE self, VALUE prefix, VALUE href)
1058
+ {
1059
+ xmlNodePtr node, namespacee;
1060
+ xmlNsPtr ns;
1061
+
1062
+ Data_Get_Struct(self, xmlNode, node);
1063
+ namespacee = node ;
1064
+
1065
+ ns = xmlSearchNs(
1066
+ node->doc,
1067
+ node,
1068
+ (const xmlChar *)(NIL_P(prefix) ? NULL : StringValuePtr(prefix))
1069
+ );
1070
+
1071
+ if(!ns) {
1072
+ if (node->type != XML_ELEMENT_NODE) {
1073
+ namespacee = node->parent;
1074
+ }
1075
+ ns = xmlNewNs(
1076
+ namespacee,
1077
+ (const xmlChar *)StringValuePtr(href),
1078
+ (const xmlChar *)(NIL_P(prefix) ? NULL : StringValuePtr(prefix))
1079
+ );
1080
+ }
1081
+
1082
+ if (!ns) return Qnil ;
1083
+
1084
+ if(NIL_P(prefix) || node != namespacee) xmlSetNs(node, ns);
1085
+
1086
+ return Nokogiri_wrap_xml_namespace(node->doc, ns);
1087
+ }
1088
+
1089
+ /*
1090
+ * call-seq:
1091
+ * new(name, document)
1092
+ *
1093
+ * Create a new node with +name+ sharing GC lifecycle with +document+
1094
+ */
1095
+ static VALUE new(int argc, VALUE *argv, VALUE klass)
1096
+ {
1097
+ xmlDocPtr doc;
1098
+ xmlNodePtr node;
1099
+ VALUE name;
1100
+ VALUE document;
1101
+ VALUE rest;
1102
+ VALUE rb_node;
1103
+
1104
+ rb_scan_args(argc, argv, "2*", &name, &document, &rest);
1105
+
1106
+ Data_Get_Struct(document, xmlDoc, doc);
1107
+
1108
+ node = xmlNewNode(NULL, (xmlChar *)StringValuePtr(name));
1109
+ node->doc = doc->doc;
1110
+ NOKOGIRI_ROOT_NODE(node);
1111
+
1112
+ rb_node = Nokogiri_wrap_xml_node(
1113
+ klass == cNokogiriXmlNode ? Qnil : klass,
1114
+ node
1115
+ );
1116
+ rb_obj_call_init(rb_node, argc, argv);
1117
+
1118
+ if(rb_block_given_p()) rb_yield(rb_node);
1119
+
1120
+ return rb_node;
1121
+ }
1122
+
1123
+ /*
1124
+ * call-seq:
1125
+ * dump_html
1126
+ *
1127
+ * Returns the Node as html.
1128
+ */
1129
+ static VALUE dump_html(VALUE self)
1130
+ {
1131
+ xmlBufferPtr buf ;
1132
+ xmlNodePtr node ;
1133
+ VALUE html;
1134
+
1135
+ Data_Get_Struct(self, xmlNode, node);
1136
+
1137
+ buf = xmlBufferCreate() ;
1138
+ htmlNodeDump(buf, node->doc, node);
1139
+ html = NOKOGIRI_STR_NEW2(buf->content);
1140
+ xmlBufferFree(buf);
1141
+ return html ;
1142
+ }
1143
+
1144
+ /*
1145
+ * call-seq:
1146
+ * compare(other)
1147
+ *
1148
+ * Compare this Node to +other+ with respect to their Document
1149
+ */
1150
+ static VALUE compare(VALUE self, VALUE _other)
1151
+ {
1152
+ xmlNodePtr node, other;
1153
+ Data_Get_Struct(self, xmlNode, node);
1154
+ Data_Get_Struct(_other, xmlNode, other);
1155
+
1156
+ return INT2NUM((long)xmlXPathCmpNodes(other, node));
1157
+ }
1158
+
1159
+
1160
+ /* TODO: DOCUMENT ME */
1161
+ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1162
+ {
1163
+ xmlNodePtr node;
1164
+ xmlNodePtr list;
1165
+ xmlNodePtr child_iter;
1166
+ xmlNodeSetPtr set;
1167
+ xmlParserErrors error;
1168
+ VALUE doc, err;
1169
+
1170
+ Data_Get_Struct(self, xmlNode, node);
1171
+
1172
+ doc = DOC_RUBY_OBJECT(node->doc);
1173
+ err = rb_iv_get(doc, "@errors");
1174
+
1175
+ xmlSetStructuredErrorFunc((void *)err, Nokogiri_error_array_pusher);
1176
+
1177
+ /* Twiddle global variable because of a bug in libxml2.
1178
+ * http://git.gnome.org/browse/libxml2/commit/?id=e20fb5a72c83cbfc8e4a8aa3943c6be8febadab7
1179
+ */
1180
+ #ifndef HTML_PARSE_NOIMPLIED
1181
+ htmlHandleOmittedElem(0);
1182
+ #endif
1183
+
1184
+ error = xmlParseInNodeContext(
1185
+ node,
1186
+ StringValuePtr(_str),
1187
+ (int)RSTRING_LEN(_str),
1188
+ (int)NUM2INT(_options),
1189
+ &list);
1190
+
1191
+ /* make sure parent/child pointers are coherent so an unlink will work properly (#331) */
1192
+ child_iter = node->doc->children ;
1193
+ while (child_iter) {
1194
+ if (child_iter->parent != (xmlNodePtr)node->doc)
1195
+ child_iter->parent = (xmlNodePtr)node->doc ;
1196
+ child_iter = child_iter->next ;
1197
+ }
1198
+
1199
+ #ifndef HTML_PARSE_NOIMPLIED
1200
+ htmlHandleOmittedElem(1);
1201
+ #endif
1202
+
1203
+ xmlSetStructuredErrorFunc(NULL, NULL);
1204
+
1205
+ /* FIXME: This probably needs to handle more constants... */
1206
+ switch(error) {
1207
+ case XML_ERR_OK:
1208
+ break;
1209
+
1210
+ case XML_ERR_INTERNAL_ERROR:
1211
+ case XML_ERR_NO_MEMORY:
1212
+ rb_raise(rb_eRuntimeError, "error parsing fragment (%d)", error);
1213
+ break;
1214
+
1215
+ default:
1216
+ break;
1217
+ }
1218
+
1219
+ set = xmlXPathNodeSetCreate(NULL);
1220
+
1221
+ while(list) {
1222
+ xmlXPathNodeSetAddUnique(set, list);
1223
+ list = list->next;
1224
+ }
1225
+
1226
+ return Nokogiri_wrap_xml_node_set(set, doc);
1227
+ }
1228
+
1229
+ static VALUE sym_iv_doc = Qnil;
1230
+
1231
+
1232
+ VALUE Nokogiri_wrap_xml_node(VALUE klass, xmlNodePtr node)
1233
+ {
1234
+ VALUE document = Qnil ;
1235
+ VALUE node_cache = Qnil ;
1236
+ VALUE rb_node = Qnil ;
1237
+ int node_has_a_document = 0 ;
1238
+ void (*mark_method)(xmlNodePtr) = NULL ;
1239
+
1240
+ assert(node);
1241
+
1242
+ if(node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE)
1243
+ return DOC_RUBY_OBJECT(node->doc);
1244
+
1245
+ if(NULL != node->_private) return (VALUE)node->_private;
1246
+
1247
+ if (!RTEST(klass)) {
1248
+ switch(node->type) {
1249
+
1250
+ case XML_ELEMENT_NODE:
1251
+ klass = cNokogiriXmlElement;
1252
+ break;
1253
+ case XML_TEXT_NODE:
1254
+ klass = cNokogiriXmlText;
1255
+ break;
1256
+ case XML_ATTRIBUTE_NODE:
1257
+ klass = cNokogiriXmlAttr;
1258
+ break;
1259
+ case XML_ENTITY_REF_NODE:
1260
+ klass = cNokogiriXmlEntityReference;
1261
+ break;
1262
+ case XML_COMMENT_NODE:
1263
+ klass = cNokogiriXmlComment;
1264
+ break;
1265
+ case XML_DOCUMENT_FRAG_NODE:
1266
+ klass = cNokogiriXmlDocumentFragment;
1267
+ break;
1268
+ case XML_PI_NODE:
1269
+ klass = cNokogiriXmlProcessingInstruction;
1270
+ break;
1271
+ case XML_ENTITY_DECL:
1272
+ klass = cNokogiriXmlEntityDecl;
1273
+ break;
1274
+ case XML_CDATA_SECTION_NODE:
1275
+ klass = cNokogiriXmlCData;
1276
+ break;
1277
+ case XML_DTD_NODE:
1278
+ klass = cNokogiriXmlDtd;
1279
+ break;
1280
+ case XML_ATTRIBUTE_DECL:
1281
+ klass = cNokogiriXmlAttributeDecl;
1282
+ break;
1283
+ case XML_ELEMENT_DECL:
1284
+ klass = cNokogiriXmlElementDecl;
1285
+ break;
1286
+ default:
1287
+ klass = cNokogiriXmlNode;
1288
+ }
1289
+ }
1290
+
1291
+ /* It's OK if the node doesn't have a fully-realized document (as in XML::Reader). */
1292
+ /* see https://github.com/tenderlove/nokogiri/issues/95 */
1293
+ /* and https://github.com/tenderlove/nokogiri/issues/439 */
1294
+ node_has_a_document = (DOC_RUBY_OBJECT_TEST(node->doc) && DOC_RUBY_OBJECT(node->doc)) ? 1 : 0 ;
1295
+
1296
+ if (DOC_RUBY_OBJECT_TEST(node->doc)) { // maglev workaround , no gc mark
1297
+ VALUE ref = DOC_RUBY_OBJECT(node->doc);
1298
+ if (ref != 0 && ref != Qnil && sym_iv_doc != Qnil) {
1299
+ rb_node = Data_Wrap_Struct(klass, NULL, debug_node_dealloc, node) ;
1300
+ rb_ivar_set(rb_node, sym_iv_doc, ref);
1301
+ }
1302
+ }
1303
+ if (rb_node == Qnil ) { // maglev will fail, rb_gc_mark not supported
1304
+ rb_node = Data_Wrap_Struct(klass, mark, debug_node_dealloc, node) ;
1305
+ }
1306
+
1307
+ node->_private = (void *)rb_node;
1308
+
1309
+ if (node_has_a_document) {
1310
+ document = DOC_RUBY_OBJECT(node->doc);
1311
+ node_cache = DOC_NODE_CACHE(node->doc);
1312
+ rb_ary_push(node_cache, rb_node);
1313
+ rb_funcall(document, decorate, 1, rb_node);
1314
+ }
1315
+
1316
+ return rb_node ;
1317
+ }
1318
+
1319
+
1320
+ void Nokogiri_xml_node_properties(xmlNodePtr node, VALUE attr_list)
1321
+ {
1322
+ xmlAttrPtr prop;
1323
+ prop = node->properties ;
1324
+ while (prop != NULL) {
1325
+ rb_ary_push(attr_list, Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop));
1326
+ prop = prop->next ;
1327
+ }
1328
+ }
1329
+
1330
+ VALUE cNokogiriXmlNode ;
1331
+ VALUE cNokogiriXmlElement ;
1332
+
1333
+ void init_xml_node()
1334
+ {
1335
+ VALUE nokogiri = rb_define_module("Nokogiri");
1336
+ VALUE xml = rb_define_module_under(nokogiri, "XML");
1337
+ VALUE klass = rb_define_class_under(xml, "Node", rb_cObject);
1338
+
1339
+ cNokogiriXmlNode = klass;
1340
+
1341
+ cNokogiriXmlElement = rb_define_class_under(xml, "Element", klass);
1342
+ sym_iv_doc = rb_intern("@doc");
1343
+
1344
+ rb_define_singleton_method(klass, "new", new, -1);
1345
+
1346
+ rb_define_method(klass, "add_namespace_definition", add_namespace_definition, 2);
1347
+ rb_define_method(klass, "node_name", get_name, 0);
1348
+ rb_define_method(klass, "document", document, 0);
1349
+ rb_define_method(klass, "node_name=", set_name, 1);
1350
+ rb_define_method(klass, "parent", get_parent, 0);
1351
+ rb_define_method(klass, "child", child, 0);
1352
+ rb_define_method(klass, "first_element_child", first_element_child, 0);
1353
+ rb_define_method(klass, "last_element_child", last_element_child, 0);
1354
+ rb_define_method(klass, "children", children, 0);
1355
+ rb_define_method(klass, "element_children", element_children, 0);
1356
+ rb_define_method(klass, "next_sibling", next_sibling, 0);
1357
+ rb_define_method(klass, "previous_sibling", previous_sibling, 0);
1358
+ rb_define_method(klass, "next_element", next_element, 0);
1359
+ rb_define_method(klass, "previous_element", previous_element, 0);
1360
+ rb_define_method(klass, "node_type", node_type, 0);
1361
+ rb_define_method(klass, "content", get_content, 0);
1362
+ rb_define_method(klass, "path", path, 0);
1363
+ rb_define_method(klass, "key?", key_eh, 1);
1364
+ rb_define_method(klass, "namespaced_key?", namespaced_key_eh, 2);
1365
+ rb_define_method(klass, "blank?", blank_eh, 0);
1366
+ rb_define_method(klass, "[]=", set, 2);
1367
+ rb_define_method(klass, "attribute_nodes", attribute_nodes, 0);
1368
+ rb_define_method(klass, "attribute", attr, 1);
1369
+ rb_define_method(klass, "attribute_with_ns", attribute_with_ns, 2);
1370
+ rb_define_method(klass, "namespace", namespace, 0);
1371
+ rb_define_method(klass, "namespace_definitions", namespace_definitions, 0);
1372
+ rb_define_method(klass, "namespace_scopes", namespace_scopes, 0);
1373
+ rb_define_method(klass, "encode_special_chars", encode_special_chars, 1);
1374
+ rb_define_method(klass, "dup", duplicate_node, -1);
1375
+ rb_define_method(klass, "unlink", unlink_node, 0);
1376
+ rb_define_method(klass, "internal_subset", internal_subset, 0);
1377
+ rb_define_method(klass, "external_subset", external_subset, 0);
1378
+ rb_define_method(klass, "create_internal_subset", create_internal_subset, 3);
1379
+ rb_define_method(klass, "create_external_subset", create_external_subset, 3);
1380
+ rb_define_method(klass, "pointer_id", pointer_id, 0);
1381
+ rb_define_method(klass, "line", line, 0);
1382
+
1383
+ rb_define_private_method(klass, "in_context", in_context, 2);
1384
+ rb_define_private_method(klass, "add_child_node", add_child, 1);
1385
+ rb_define_private_method(klass, "add_previous_sibling_node", add_previous_sibling, 1);
1386
+ rb_define_private_method(klass, "add_next_sibling_node", add_next_sibling, 1);
1387
+ rb_define_private_method(klass, "replace_node", replace, 1);
1388
+ rb_define_private_method(klass, "dump_html", dump_html, 0);
1389
+ rb_define_private_method(klass, "native_write_to", native_write_to, 4);
1390
+ rb_define_private_method(klass, "native_content=", set_content, 1);
1391
+ rb_define_private_method(klass, "get", get, 1);
1392
+ rb_define_private_method(klass, "set_namespace", set_namespace, 1);
1393
+ rb_define_private_method(klass, "compare", compare, 1);
1394
+
1395
+ decorate = rb_intern("decorate");
1396
+ decorate_bang = rb_intern("decorate!");
1397
+ }