nokogiri-backupify 1.5.0.beta.4

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