nokogiri 1.2.3-x86-mswin32-60 → 1.4.5-x86-mswin32-60

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