libxml-ruby 4.1.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/{HISTORY → CHANGELOG.md} +875 -887
  3. data/LICENSE +20 -20
  4. data/README.md +67 -0
  5. data/Rakefile +77 -98
  6. data/ext/libxml/extconf.rb +39 -20
  7. data/ext/libxml/libxml.c +67 -79
  8. data/ext/libxml/ruby_libxml.h +43 -44
  9. data/ext/libxml/ruby_xml.c +556 -899
  10. data/ext/libxml/ruby_xml.h +9 -10
  11. data/ext/libxml/ruby_xml_attr.c +338 -333
  12. data/ext/libxml/ruby_xml_attr.h +13 -12
  13. data/ext/libxml/ruby_xml_attr_decl.c +19 -12
  14. data/ext/libxml/ruby_xml_attr_decl.h +11 -11
  15. data/ext/libxml/ruby_xml_attributes.c +18 -9
  16. data/ext/libxml/ruby_xml_attributes.h +17 -15
  17. data/ext/libxml/ruby_xml_document.c +1142 -1129
  18. data/ext/libxml/ruby_xml_document.h +12 -11
  19. data/ext/libxml/ruby_xml_dtd.c +287 -248
  20. data/ext/libxml/ruby_xml_dtd.h +10 -9
  21. data/ext/libxml/ruby_xml_encoding.c +250 -250
  22. data/ext/libxml/ruby_xml_encoding.h +20 -18
  23. data/ext/libxml/ruby_xml_error.c +1003 -996
  24. data/ext/libxml/ruby_xml_error.h +14 -14
  25. data/ext/libxml/ruby_xml_html_parser.c +91 -91
  26. data/ext/libxml/ruby_xml_html_parser.h +10 -10
  27. data/ext/libxml/ruby_xml_html_parser_context.c +364 -337
  28. data/ext/libxml/ruby_xml_html_parser_context.h +11 -10
  29. data/ext/libxml/ruby_xml_html_parser_options.c +48 -48
  30. data/ext/libxml/ruby_xml_html_parser_options.h +10 -10
  31. data/ext/libxml/ruby_xml_input_cbg.c +216 -188
  32. data/ext/libxml/ruby_xml_input_cbg.h +20 -20
  33. data/ext/libxml/ruby_xml_io.c +49 -47
  34. data/ext/libxml/ruby_xml_io.h +10 -10
  35. data/ext/libxml/ruby_xml_namespace.c +159 -154
  36. data/ext/libxml/ruby_xml_namespace.h +11 -10
  37. data/ext/libxml/ruby_xml_namespaces.c +313 -293
  38. data/ext/libxml/ruby_xml_namespaces.h +9 -9
  39. data/ext/libxml/ruby_xml_node.c +1433 -1398
  40. data/ext/libxml/ruby_xml_node.h +15 -13
  41. data/ext/libxml/ruby_xml_parser.c +91 -91
  42. data/ext/libxml/ruby_xml_parser.h +10 -10
  43. data/ext/libxml/ruby_xml_parser_context.c +1016 -1001
  44. data/ext/libxml/ruby_xml_parser_context.h +11 -10
  45. data/ext/libxml/ruby_xml_parser_options.c +74 -66
  46. data/ext/libxml/ruby_xml_parser_options.h +10 -10
  47. data/ext/libxml/ruby_xml_reader.c +40 -31
  48. data/ext/libxml/ruby_xml_reader.h +14 -14
  49. data/ext/libxml/ruby_xml_registry.c +31 -0
  50. data/ext/libxml/ruby_xml_registry.h +22 -0
  51. data/ext/libxml/ruby_xml_relaxng.c +21 -5
  52. data/ext/libxml/ruby_xml_relaxng.h +9 -8
  53. data/ext/libxml/ruby_xml_sax2_handler.c +326 -326
  54. data/ext/libxml/ruby_xml_sax2_handler.h +10 -10
  55. data/ext/libxml/ruby_xml_sax_parser.c +108 -116
  56. data/ext/libxml/ruby_xml_sax_parser.h +10 -10
  57. data/ext/libxml/ruby_xml_schema.c +22 -15
  58. data/ext/libxml/ruby_xml_schema.h +26 -25
  59. data/ext/libxml/ruby_xml_schema_attribute.c +7 -7
  60. data/ext/libxml/ruby_xml_schema_attribute.h +37 -37
  61. data/ext/libxml/ruby_xml_schema_element.c +8 -8
  62. data/ext/libxml/ruby_xml_schema_element.h +11 -11
  63. data/ext/libxml/ruby_xml_schema_facet.c +50 -50
  64. data/ext/libxml/ruby_xml_schema_facet.h +9 -9
  65. data/ext/libxml/ruby_xml_schema_type.c +12 -19
  66. data/ext/libxml/ruby_xml_schema_type.h +9 -9
  67. data/ext/libxml/ruby_xml_version.h +9 -9
  68. data/ext/libxml/ruby_xml_writer.c +183 -185
  69. data/ext/libxml/ruby_xml_writer.h +6 -6
  70. data/ext/libxml/ruby_xml_xinclude.c +20 -20
  71. data/ext/libxml/ruby_xml_xinclude.h +11 -11
  72. data/ext/libxml/ruby_xml_xpath.c +195 -195
  73. data/ext/libxml/ruby_xml_xpath.h +15 -15
  74. data/ext/libxml/ruby_xml_xpath_context.c +408 -362
  75. data/ext/libxml/ruby_xml_xpath_context.h +9 -9
  76. data/ext/libxml/ruby_xml_xpath_expression.c +12 -6
  77. data/ext/libxml/ruby_xml_xpath_expression.h +11 -10
  78. data/ext/libxml/ruby_xml_xpath_object.c +69 -54
  79. data/ext/libxml/ruby_xml_xpath_object.h +19 -17
  80. data/ext/vc/libxml_ruby/libxml_ruby.vcxproj +271 -0
  81. data/ext/xcode/libxml-ruby.xcodeproj/project.pbxproj +633 -0
  82. data/ext/xcode/libxml-ruby.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  83. data/ext/xcode/libxml-ruby.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  84. data/ext/xcode/libxml-ruby.xcodeproj/xcshareddata/xcschemes/libxml-ruby.xcscheme +80 -0
  85. data/lib/libxml/attr.rb +122 -122
  86. data/lib/libxml/attr_decl.rb +80 -80
  87. data/lib/libxml/attributes.rb +13 -13
  88. data/lib/libxml/document.rb +165 -194
  89. data/lib/libxml/error.rb +122 -95
  90. data/lib/libxml/html_parser.rb +80 -96
  91. data/lib/libxml/namespace.rb +61 -61
  92. data/lib/libxml/namespaces.rb +37 -37
  93. data/lib/libxml/node.rb +321 -323
  94. data/lib/libxml/parser.rb +94 -100
  95. data/lib/libxml/sax_callbacks.rb +179 -179
  96. data/lib/libxml/sax_parser.rb +40 -40
  97. data/lib/libxml/schema/attribute.rb +27 -19
  98. data/lib/libxml/schema/element.rb +20 -0
  99. data/lib/libxml/schema/type.rb +44 -21
  100. data/lib/libxml/schema.rb +47 -47
  101. data/lib/libxml/tree.rb +28 -28
  102. data/lib/libxml-ruby.rb +30 -30
  103. data/libxml-ruby.gemspec +46 -48
  104. data/test/c14n/result/1-1-without-comments/example-1 +3 -3
  105. data/test/c14n/result/1-1-without-comments/example-2 +10 -10
  106. data/test/c14n/result/1-1-without-comments/example-3 +13 -13
  107. data/test/c14n/result/1-1-without-comments/example-4 +8 -8
  108. data/test/c14n/result/1-1-without-comments/example-5 +2 -2
  109. data/test/c14n/result/with-comments/example-1 +5 -5
  110. data/test/c14n/result/with-comments/example-2 +10 -10
  111. data/test/c14n/result/with-comments/example-3 +13 -13
  112. data/test/c14n/result/with-comments/example-4 +8 -8
  113. data/test/c14n/result/with-comments/example-5 +3 -3
  114. data/test/c14n/result/without-comments/example-1 +3 -3
  115. data/test/c14n/result/without-comments/example-2 +10 -10
  116. data/test/c14n/result/without-comments/example-3 +13 -13
  117. data/test/c14n/result/without-comments/example-4 +8 -8
  118. data/test/c14n/result/without-comments/example-5 +2 -2
  119. data/test/test_attr.rb +179 -180
  120. data/test/test_attr_decl.rb +131 -131
  121. data/test/test_attributes.rb +135 -135
  122. data/test/test_canonicalize.rb +122 -120
  123. data/test/test_document.rb +138 -132
  124. data/test/test_document_write.rb +142 -145
  125. data/test/test_dtd.rb +134 -129
  126. data/test/test_encoding.rb +126 -129
  127. data/test/test_encoding_sax.rb +114 -114
  128. data/test/test_error.rb +197 -194
  129. data/test/test_helper.rb +21 -13
  130. data/test/test_html_parser.rb +166 -162
  131. data/test/test_html_parser_context.rb +22 -22
  132. data/test/test_input_callbacks.rb +36 -0
  133. data/test/test_namespace.rb +58 -60
  134. data/test/test_namespaces.rb +200 -200
  135. data/test/test_node.rb +251 -237
  136. data/test/test_node_cdata.rb +50 -50
  137. data/test/test_node_comment.rb +32 -32
  138. data/test/test_node_copy.rb +40 -40
  139. data/test/test_node_edit.rb +176 -158
  140. data/test/test_node_pi.rb +37 -37
  141. data/test/test_node_text.rb +69 -69
  142. data/test/test_node_write.rb +93 -96
  143. data/test/test_node_xlink.rb +28 -28
  144. data/test/test_parser.rb +297 -324
  145. data/test/test_parser_context.rb +198 -198
  146. data/test/test_properties.rb +38 -38
  147. data/test/test_reader.rb +413 -364
  148. data/test/test_relaxng.rb +59 -53
  149. data/test/test_sax_parser.rb +345 -326
  150. data/test/test_schema.rb +28 -0
  151. data/test/test_traversal.rb +152 -152
  152. data/test/test_writer.rb +469 -468
  153. data/test/test_xinclude.rb +20 -20
  154. data/test/test_xml.rb +164 -267
  155. data/test/test_xpath.rb +244 -244
  156. data/test/test_xpath_context.rb +87 -87
  157. data/test/test_xpath_expression.rb +37 -37
  158. metadata +49 -31
  159. data/README.rdoc +0 -217
  160. data/ext/libxml/extconf.h +0 -3
  161. data/ext/libxml/ruby_xml_cbg.c +0 -85
  162. data/lib/libxml/hpricot.rb +0 -78
  163. data/lib/libxml.rb +0 -5
  164. data/lib/xml/libxml.rb +0 -10
  165. data/lib/xml.rb +0 -14
  166. data/script/benchmark/depixelate +0 -634
  167. data/script/benchmark/hamlet.xml +0 -9055
  168. data/script/benchmark/parsecount +0 -170
  169. data/script/benchmark/sock_entries.xml +0 -507
  170. data/script/benchmark/throughput +0 -41
  171. data/script/test +0 -6
  172. data/test/test_deprecated_require.rb +0 -12
@@ -1,1398 +1,1433 @@
1
- #include "ruby_libxml.h"
2
- #include "ruby_xml_node.h"
3
- #include <assert.h>
4
-
5
- #include <libxml/debugXML.h>
6
- #include <libxml/parserInternals.h>
7
- #include <libxml/xlink.h>
8
-
9
- VALUE cXMLNode;
10
-
11
- /* Document-class: LibXML::XML::Node
12
- *
13
- * Nodes are the primary objects that make up an XML document.
14
- * The node class represents most node types that are found in
15
- * an XML document (but not LibXML::XML::Attributes, see LibXML::XML::Attr).
16
- * It exposes libxml's full API for creating, querying
17
- * moving and deleting node objects. Many of these methods are
18
- * documented in the DOM Level 3 specification found at:
19
- * http://www.w3.org/TR/DOM-Level-3-Core/. */
20
-
21
-
22
- /* Memory management:
23
- *
24
- * The bindings create a one-to-one mapping between ruby objects and
25
- * libxml documents and libxml parent nodes (ie, nodes that do not
26
- * have a parent and do not belong to a document). In these cases,
27
- * the bindings manage the memory. They do this by installing a free
28
- * function and storing a back pointer to the Ruby object from the xmlnode
29
- * using the _private member on libxml structures. When the Ruby object
30
- * goes out of scope, the underlying libxml structure is freed. Libxml
31
- * itself then frees all child node (recursively).
32
- *
33
- * For all other nodes (the vast majority), the bindings create temporary
34
- * Ruby objects that get freed once they go out of scope. Thus there can be
35
- * more than one ruby object pointing to the same xml node. To mostly hide
36
- * this from programmers on the ruby side, the #eql? and #== methods are
37
- * overriden to check if two ruby objects wrap the same xmlnode. If they do,
38
- * then the methods return true. During the mark phase, each of these temporary
39
- * objects marks its owning document, thereby keeping the Ruby document object
40
- * alive and thus the xmldoc tree.
41
- *
42
- * In the sweep phase of the garbage collector, or when a program ends,
43
- * there is no order to how Ruby objects are freed. In fact, the ruby document
44
- * object is almost always freed before any ruby objects that wrap child nodes.
45
- * However, this is ok because those ruby objects do not have a free function
46
- * and are no longer in scope (since if they were the document would not be freed).
47
- */
48
-
49
- static void rxml_node_free(xmlNodePtr xnode)
50
- {
51
- /* The ruby object wrapping the xml object no longer exists and this
52
- is a standalone node without a document or parent so ruby is
53
- responsible for freeing the underlying node.*/
54
- if (xnode->doc == NULL && xnode->parent == NULL)
55
- {
56
- // Remove the back linkage from libxml to Ruby
57
- xnode->_private = NULL;
58
- xmlFreeNode(xnode);
59
- }
60
- }
61
-
62
- void rxml_node_manage(xmlNodePtr xnode, VALUE node)
63
- {
64
- RDATA(node)->dfree = (RUBY_DATA_FUNC)rxml_node_free;
65
- xnode->_private = (void*)node;
66
- }
67
-
68
- void rxml_node_unmanage(xmlNodePtr xnode, VALUE node)
69
- {
70
- RDATA(node)->dfree = NULL;
71
- xnode->_private = NULL;
72
- }
73
-
74
- xmlNodePtr rxml_node_root(xmlNodePtr xnode)
75
- {
76
- xmlNodePtr current = xnode;
77
-
78
- while (current->parent)
79
- {
80
- current = current->parent;
81
- }
82
-
83
- return current;
84
- }
85
-
86
- void rxml_node_mark(xmlNodePtr xnode)
87
- {
88
- if (xnode->doc)
89
- {
90
- if (xnode->doc->_private)
91
- {
92
- VALUE doc = (VALUE)xnode->doc->_private;
93
- rb_gc_mark(doc);
94
- }
95
- }
96
- else if (xnode->parent)
97
- {
98
- xmlNodePtr root = rxml_node_root(xnode);
99
- if (root->_private)
100
- {
101
- VALUE node = (VALUE)root->_private;
102
- rb_gc_mark(node);
103
- }
104
- }
105
- }
106
-
107
- VALUE rxml_node_wrap(xmlNodePtr xnode)
108
- {
109
- VALUE result = Qnil;
110
-
111
- // Is this node already wrapped?
112
- if (xnode->_private)
113
- {
114
- result = (VALUE)xnode->_private;
115
- }
116
- else
117
- {
118
- result = Data_Wrap_Struct(cXMLNode, rxml_node_mark, NULL, xnode);
119
- }
120
-
121
- if (!xnode->doc && !xnode->parent)
122
- {
123
- rxml_node_manage(xnode, result);
124
- }
125
- return result;
126
- }
127
-
128
- static VALUE rxml_node_alloc(VALUE klass)
129
- {
130
- // This node was created from Ruby so we are responsible for freeing it not libxml
131
- return Data_Wrap_Struct(klass, rxml_node_mark, rxml_node_free, NULL);
132
- }
133
-
134
- static xmlNodePtr rxml_get_xnode(VALUE node)
135
- {
136
- xmlNodePtr result;
137
- Data_Get_Struct(node, xmlNode, result);
138
-
139
- if (!result)
140
- rb_raise(rb_eRuntimeError, "This node has already been freed.");
141
-
142
- return result;
143
- }
144
-
145
- /*
146
- * call-seq:
147
- * XML::Node.new_cdata(content = nil) -> XML::Node
148
- *
149
- * Create a new #CDATA node, optionally setting
150
- * the node's content.
151
- */
152
- static VALUE rxml_node_new_cdata(int argc, VALUE *argv, VALUE klass)
153
- {
154
- VALUE content = Qnil;
155
- xmlNodePtr xnode;
156
-
157
- rb_scan_args(argc, argv, "01", &content);
158
-
159
- if (NIL_P(content))
160
- {
161
- xnode = xmlNewCDataBlock(NULL, NULL, 0);
162
- }
163
- else
164
- {
165
- content = rb_obj_as_string(content);
166
- xnode = xmlNewCDataBlock(NULL, (xmlChar*) StringValuePtr(content), (int)RSTRING_LEN(content));
167
- }
168
-
169
- if (xnode == NULL)
170
- rxml_raise(&xmlLastError);
171
-
172
- return rxml_node_wrap(xnode);
173
- }
174
-
175
- /*
176
- * call-seq:
177
- * XML::Node.new_comment(content = nil) -> XML::Node
178
- *
179
- * Create a new comment node, optionally setting
180
- * the node's content.
181
- *
182
- */
183
- static VALUE rxml_node_new_comment(int argc, VALUE *argv, VALUE klass)
184
- {
185
- VALUE content = Qnil;
186
- xmlNodePtr xnode;
187
-
188
- rb_scan_args(argc, argv, "01", &content);
189
-
190
- if (NIL_P(content))
191
- {
192
- xnode = xmlNewComment(NULL);
193
- }
194
- else
195
- {
196
- content = rb_obj_as_string(content);
197
- xnode = xmlNewComment((xmlChar*) StringValueCStr(content));
198
- }
199
-
200
- if (xnode == NULL)
201
- rxml_raise(&xmlLastError);
202
-
203
- return rxml_node_wrap(xnode);
204
- }
205
-
206
- /*
207
- * call-seq:
208
- * XML::Node.new_pi(name, content = nil) -> XML::Node
209
- *
210
- * Create a new pi node, optionally setting
211
- * the node's content.
212
- *
213
- */
214
- static VALUE rxml_node_new_pi(int argc, VALUE *argv, VALUE klass)
215
- {
216
- VALUE name = Qnil;
217
- VALUE content = Qnil;
218
- xmlNodePtr xnode;
219
-
220
- rb_scan_args(argc, argv, "11", &name, &content);
221
-
222
- if (NIL_P(name))
223
- {
224
- rb_raise(rb_eRuntimeError, "You must provide me with a name for a PI.");
225
- }
226
- name = rb_obj_as_string(name);
227
- if (NIL_P(content))
228
- {
229
- xnode = xmlNewPI((xmlChar*) StringValuePtr(name), NULL);
230
- }
231
- else
232
- {
233
- content = rb_obj_as_string(content);
234
- xnode = xmlNewPI((xmlChar*) StringValuePtr(name), (xmlChar*) StringValueCStr(content));
235
- }
236
-
237
- if (xnode == NULL)
238
- rxml_raise(&xmlLastError);
239
-
240
- return rxml_node_wrap(xnode);
241
- }
242
-
243
- /*
244
- * call-seq:
245
- * XML::Node.new_text(content) -> XML::Node
246
- *
247
- * Create a new text node.
248
- *
249
- */
250
- static VALUE rxml_node_new_text(VALUE klass, VALUE content)
251
- {
252
- xmlNodePtr xnode;
253
- Check_Type(content, T_STRING);
254
- content = rb_obj_as_string(content);
255
-
256
- xnode = xmlNewText((xmlChar*) StringValueCStr(content));
257
-
258
- if (xnode == NULL)
259
- rxml_raise(&xmlLastError);
260
-
261
- return rxml_node_wrap(xnode);
262
- }
263
-
264
- static VALUE rxml_node_content_set(VALUE self, VALUE content);
265
-
266
- /*
267
- * call-seq:
268
- * XML::Node.initialize(name, content = nil, namespace = nil) -> XML::Node
269
- *
270
- * Creates a new element with the specified name, content and
271
- * namespace. The content and namespace may be nil.
272
- */
273
- static VALUE rxml_node_initialize(int argc, VALUE *argv, VALUE self)
274
- {
275
- VALUE name;
276
- VALUE content;
277
- VALUE ns;
278
- xmlNodePtr xnode = NULL;
279
- xmlNsPtr xns = NULL;
280
-
281
- rb_scan_args(argc, argv, "12", &name, &content, &ns);
282
-
283
- name = rb_obj_as_string(name);
284
-
285
- if (!NIL_P(ns))
286
- Data_Get_Struct(ns, xmlNs, xns);
287
-
288
- xnode = xmlNewNode(xns, (xmlChar*) StringValuePtr(name));
289
-
290
- if (xnode == NULL)
291
- rxml_raise(&xmlLastError);
292
-
293
- // Link the ruby wrapper to the underlying libxml node
294
- RDATA(self)->data = xnode;
295
-
296
- // Ruby is in charge of managing this node's memory
297
- rxml_node_manage(xnode, self);
298
-
299
- if (!NIL_P(content))
300
- rxml_node_content_set(self, content);
301
-
302
- return self;
303
- }
304
-
305
- static VALUE rxml_node_modify_dom(VALUE self, VALUE target,
306
- xmlNodePtr (*xmlFunc)(xmlNodePtr, xmlNodePtr))
307
- {
308
- xmlNodePtr xnode, xtarget, xresult;
309
-
310
- if (rb_obj_is_kind_of(target, cXMLNode) == Qfalse)
311
- rb_raise(rb_eTypeError, "Must pass an XML::Node object");
312
-
313
- xnode = rxml_get_xnode(self);
314
- xtarget = rxml_get_xnode(target);
315
-
316
- if (xtarget->doc != NULL && xtarget->doc != xnode->doc)
317
- rb_raise(eXMLError, "Nodes belong to different documents. You must first import the node by calling LibXML::XML::Document.import");
318
-
319
- xmlUnlinkNode(xtarget);
320
-
321
- // Target is about to have a parent, so stop having ruby manage it.
322
- rxml_node_unmanage(xtarget, target);
323
-
324
- // This target node could be freed here and be replaced by a different node
325
- xresult = xmlFunc(xnode, xtarget);
326
-
327
- if (!xresult)
328
- rxml_raise(&xmlLastError);
329
-
330
- /* Assume the target was freed, we need to fix up the ruby object to point to the
331
- newly returned node. */
332
- RDATA(target)->data = xresult;
333
-
334
- return target;
335
- }
336
-
337
- /*
338
- * call-seq:
339
- * node.base_uri -> "uri"
340
- *
341
- * Obtain this node's base URI.
342
- */
343
- static VALUE rxml_node_base_uri_get(VALUE self)
344
- {
345
- xmlNodePtr xnode;
346
- xmlChar* base_uri;
347
- VALUE result = Qnil;
348
-
349
- xnode = rxml_get_xnode(self);
350
-
351
- if (xnode->doc == NULL)
352
- return (result);
353
-
354
- base_uri = xmlNodeGetBase(xnode->doc, xnode);
355
- if (base_uri)
356
- {
357
- result = rxml_new_cstr( base_uri, NULL);
358
- xmlFree(base_uri);
359
- }
360
-
361
- return (result);
362
- }
363
-
364
- // TODO node_base_set should support setting back to nil
365
-
366
- /*
367
- * call-seq:
368
- * node.base_uri = "uri"
369
- *
370
- * Set this node's base URI.
371
- */
372
- static VALUE rxml_node_base_uri_set(VALUE self, VALUE uri)
373
- {
374
- xmlNodePtr xnode;
375
-
376
- Check_Type(uri, T_STRING);
377
- xnode = rxml_get_xnode(self);
378
- if (xnode->doc == NULL)
379
- return (Qnil);
380
-
381
- xmlNodeSetBase(xnode, (xmlChar*) StringValuePtr(uri));
382
- return (Qtrue);
383
- }
384
-
385
- /*
386
- * call-seq:
387
- * node.content -> "string"
388
- *
389
- * Obtain this node's content as a string.
390
- */
391
- static VALUE rxml_node_content_get(VALUE self)
392
- {
393
- xmlNodePtr xnode;
394
- xmlChar *content;
395
- VALUE result = Qnil;
396
-
397
- xnode = rxml_get_xnode(self);
398
- content = xmlNodeGetContent(xnode);
399
- if (content)
400
- {
401
- result = rxml_new_cstr(content, NULL);
402
- xmlFree(content);
403
- }
404
-
405
- return result;
406
- }
407
-
408
- /*
409
- * call-seq:
410
- * node.content = "string"
411
- *
412
- * Set this node's content to the specified string.
413
- */
414
- static VALUE rxml_node_content_set(VALUE self, VALUE content)
415
- {
416
- xmlNodePtr xnode;
417
- xmlChar* encoded_content;
418
-
419
- Check_Type(content, T_STRING);
420
- xnode = rxml_get_xnode(self);
421
- encoded_content = xmlEncodeSpecialChars(xnode->doc, (xmlChar*) StringValuePtr(content));
422
- xmlNodeSetContent(xnode, encoded_content);
423
- xmlFree(encoded_content);
424
- return (Qtrue);
425
- }
426
-
427
- /*
428
- * call-seq:
429
- * node.debug -> true|false
430
- *
431
- * Print libxml debugging information to stdout.
432
- * Requires that libxml was compiled with debugging enabled.
433
- */
434
- static VALUE rxml_node_debug(VALUE self)
435
- {
436
- #ifdef LIBXML_DEBUG_ENABLED
437
- xmlNodePtr xnode;
438
- xnode = rxml_get_xnode(self);
439
- xmlDebugDumpNode(NULL, xnode, 2);
440
- return Qtrue;
441
- #else
442
- rb_warn("libxml was compiled without debugging support.");
443
- return Qfalse;
444
- #endif
445
- }
446
-
447
- /*
448
- * call-seq:
449
- * node.first -> XML::Node
450
- *
451
- * Returns this node's first child node if any.
452
- */
453
- static VALUE rxml_node_first_get(VALUE self)
454
- {
455
- xmlNodePtr xnode;
456
-
457
- xnode = rxml_get_xnode(self);
458
-
459
- if (xnode->children)
460
- return (rxml_node_wrap(xnode->children));
461
- else
462
- return (Qnil);
463
- }
464
-
465
-
466
- /*
467
- * call-seq:
468
- * curr_node << "Some text"
469
- * curr_node << node
470
- *
471
- * Add the specified text or XML::Node as a new child node to the
472
- * current node.
473
- *
474
- * If the specified argument is a string, it should be a raw string
475
- * that contains unescaped XML special characters. Entity references
476
- * are not supported.
477
- *
478
- * The method will return the current node.
479
- */
480
- static VALUE rxml_node_content_add(VALUE self, VALUE obj)
481
- {
482
- xmlNodePtr xnode;
483
- VALUE str;
484
-
485
- xnode = rxml_get_xnode(self);
486
-
487
- /* XXX This should only be legal for a CDATA type node, I think,
488
- * resulting in a merge of content, as if a string were passed
489
- * danj 070827
490
- */
491
- if (rb_obj_is_kind_of(obj, cXMLNode))
492
- {
493
- rxml_node_modify_dom(self, obj, xmlAddChild);
494
- }
495
- else
496
- {
497
- str = rb_obj_as_string(obj);
498
- if (NIL_P(str) || TYPE(str) != T_STRING)
499
- rb_raise(rb_eTypeError, "invalid argument: must be string or XML::Node");
500
-
501
- xmlNodeAddContent(xnode, (xmlChar*) StringValuePtr(str));
502
- }
503
- return self;
504
- }
505
-
506
- /*
507
- * call-seq:
508
- * node.doc -> document
509
- *
510
- * Obtain the XML::Document this node belongs to.
511
- */
512
- static VALUE rxml_node_doc(VALUE self)
513
- {
514
- xmlDocPtr xdoc = NULL;
515
- xmlNodePtr xnode = rxml_get_xnode(self);
516
-
517
- switch (xnode->type)
518
- {
519
- case XML_DOCUMENT_NODE:
520
- #ifdef LIBXML_DOCB_ENABLED
521
- case XML_DOCB_DOCUMENT_NODE:
522
- #endif
523
- case XML_HTML_DOCUMENT_NODE:
524
- case XML_NAMESPACE_DECL:
525
- break;
526
- case XML_ATTRIBUTE_NODE:
527
- xdoc = (xmlDocPtr)((xmlAttrPtr) xnode->doc);
528
- break;
529
- default:
530
- xdoc = xnode->doc;
531
- }
532
-
533
- if (xdoc == NULL)
534
- return (Qnil);
535
-
536
- return (VALUE)xdoc->_private;
537
- }
538
-
539
- /*
540
- * call-seq:
541
- * node.to_s -> "string"
542
- * node.to_s(:indent => true, :encoding => 'UTF-8', :level => 0) -> "string"
543
- *
544
- * Converts a node, and all of its children, to a string representation.
545
- * To include only the node's children, use the the XML::Node#inner_xml
546
- * method.
547
- *
548
- * You may provide an optional hash table to control how the string is
549
- * generated. Valid options are:
550
- *
551
- * :indent - Specifies if the string should be indented. The default value
552
- * is true. Note that indentation is only added if both :indent is
553
- * true and XML.indent_tree_output is true. If :indent is set to false,
554
- * then both indentation and line feeds are removed from the result.
555
- *
556
- * :level - Specifies the indentation level. The amount of indentation
557
- * is equal to the (level * number_spaces) + number_spaces, where libxml
558
- * defaults the number of spaces to 2. Thus a level of 0 results in
559
- * 2 spaces, level 1 results in 4 spaces, level 2 results in 6 spaces, etc.
560
- *
561
- * :encoding - Specifies the output encoding of the string. It
562
- * defaults to XML::Encoding::UTF8. To change it, use one of the
563
- * XML::Encoding encoding constants. */
564
-
565
- static VALUE rxml_node_to_s(int argc, VALUE *argv, VALUE self)
566
- {
567
- VALUE result = Qnil;
568
- VALUE options = Qnil;
569
- xmlNodePtr xnode;
570
- xmlCharEncodingHandlerPtr encodingHandler;
571
- xmlOutputBufferPtr output;
572
-
573
- int level = 0;
574
- int indent = 1;
575
- const xmlChar *xencoding = (const xmlChar*)"UTF-8";
576
-
577
- rb_scan_args(argc, argv, "01", &options);
578
-
579
- if (!NIL_P(options))
580
- {
581
- VALUE rencoding, rindent, rlevel;
582
- Check_Type(options, T_HASH);
583
- rencoding = rb_hash_aref(options, ID2SYM(rb_intern("encoding")));
584
- rindent = rb_hash_aref(options, ID2SYM(rb_intern("indent")));
585
- rlevel = rb_hash_aref(options, ID2SYM(rb_intern("level")));
586
-
587
- if (rindent == Qfalse)
588
- indent = 0;
589
-
590
- if (rlevel != Qnil)
591
- level = NUM2INT(rlevel);
592
-
593
- if (rencoding != Qnil)
594
- {
595
- xencoding = (const xmlChar*)xmlGetCharEncodingName((xmlCharEncoding)NUM2INT(rencoding));
596
- if (!xencoding)
597
- rb_raise(rb_eArgError, "Unknown encoding value: %d", NUM2INT(rencoding));
598
- }
599
- }
600
-
601
- encodingHandler = xmlFindCharEncodingHandler((const char*)xencoding);
602
- output = xmlAllocOutputBuffer(encodingHandler);
603
-
604
- xnode = rxml_get_xnode(self);
605
-
606
- xmlNodeDumpOutput(output, xnode->doc, xnode, level, indent, (const char*)xencoding);
607
- xmlOutputBufferFlush(output);
608
-
609
- #ifdef LIBXML2_NEW_BUFFER
610
- if (output->conv)
611
- result = rxml_new_cstr(xmlBufContent(output->conv), xencoding);
612
- else
613
- result = rxml_new_cstr(xmlBufContent(output->buffer), xencoding);
614
- #else
615
- if (output->conv)
616
- result = rxml_new_cstr(xmlBufferContent(output->conv), xencoding);
617
- else
618
- result = rxml_new_cstr(xmlBufferContent(output->buffer), xencoding);
619
- #endif
620
-
621
- xmlOutputBufferClose(output);
622
-
623
- return result;
624
- }
625
-
626
-
627
- /*
628
- * call-seq:
629
- * node.each -> XML::Node
630
- *
631
- * Iterates over this node's children, including text
632
- * nodes, element nodes, etc. If you wish to iterate
633
- * only over child elements, use XML::Node#each_element.
634
- *
635
- * doc = XML::Document.new('model/books.xml')
636
- * doc.root.each {|node| puts node}
637
- */
638
- static VALUE rxml_node_each(VALUE self)
639
- {
640
- xmlNodePtr xnode;
641
- xmlNodePtr xcurrent;
642
- xnode = rxml_get_xnode(self);
643
-
644
- xcurrent = xnode->children;
645
-
646
- while (xcurrent)
647
- {
648
- /* The user could remove this node, so first stache
649
- away the next node. */
650
- xmlNodePtr xnext = xcurrent->next;
651
-
652
- rb_yield(rxml_node_wrap(xcurrent));
653
- xcurrent = xnext;
654
- }
655
- return Qnil;
656
- }
657
-
658
- /*
659
- * call-seq:
660
- * node.empty? -> (true|false)
661
- *
662
- * Determine whether this node is an empty or whitespace only text-node.
663
- */
664
- static VALUE rxml_node_empty_q(VALUE self)
665
- {
666
- xmlNodePtr xnode;
667
- xnode = rxml_get_xnode(self);
668
- if (xnode == NULL)
669
- return (Qnil);
670
-
671
- return ((xmlIsBlankNode(xnode) == 1) ? Qtrue : Qfalse);
672
- }
673
-
674
-
675
- /*
676
- * call-seq:
677
- * node.eql?(other_node) => (true|false)
678
- *
679
- * Test equality between the two nodes. Two nodes are equal
680
- * if they are the same node.*/
681
- static VALUE rxml_node_eql_q(VALUE self, VALUE other)
682
- {
683
- if (self == other)
684
- {
685
- return Qtrue;
686
- }
687
- else if (NIL_P(other))
688
- {
689
- return Qfalse;
690
- }
691
- else
692
- {
693
- xmlNodePtr xnode = rxml_get_xnode(self);
694
- xmlNodePtr xnode_other = rxml_get_xnode(other);
695
- return xnode == xnode_other ? Qtrue : Qfalse;
696
- }
697
- }
698
-
699
- /*
700
- * call-seq:
701
- * node.lang -> "string"
702
- *
703
- * Obtain the language set for this node, if any.
704
- * This is set in XML via the xml:lang attribute.
705
- */
706
- static VALUE rxml_node_lang_get(VALUE self)
707
- {
708
- xmlNodePtr xnode;
709
- xmlChar *lang;
710
- VALUE result = Qnil;
711
-
712
- xnode = rxml_get_xnode(self);
713
- lang = xmlNodeGetLang(xnode);
714
-
715
- if (lang)
716
- {
717
- result = rxml_new_cstr( lang, NULL);
718
- xmlFree(lang);
719
- }
720
-
721
- return (result);
722
- }
723
-
724
- // TODO node_lang_set should support setting back to nil
725
-
726
- /*
727
- * call-seq:
728
- * node.lang = "string"
729
- *
730
- * Set the language for this node. This affects the value
731
- * of the xml:lang attribute.
732
- */
733
- static VALUE rxml_node_lang_set(VALUE self, VALUE lang)
734
- {
735
- xmlNodePtr xnode;
736
-
737
- Check_Type(lang, T_STRING);
738
- xnode = rxml_get_xnode(self);
739
- xmlNodeSetLang(xnode, (xmlChar*) StringValuePtr(lang));
740
-
741
- return (Qtrue);
742
- }
743
-
744
- /*
745
- * call-seq:
746
- * node.last -> XML::Node
747
- *
748
- * Obtain the last child node of this node, if any.
749
- */
750
- static VALUE rxml_node_last_get(VALUE self)
751
- {
752
- xmlNodePtr xnode;
753
-
754
- xnode = rxml_get_xnode(self);
755
-
756
- if (xnode->last)
757
- return (rxml_node_wrap(xnode->last));
758
- else
759
- return (Qnil);
760
- }
761
-
762
- /*
763
- * call-seq:
764
- * node.line_num -> num
765
- *
766
- * Obtain the line number (in the XML document) that this
767
- * node was read from. If +default_line_numbers+ is set
768
- * false (the default), this method returns zero.
769
- */
770
- static VALUE rxml_node_line_num(VALUE self)
771
- {
772
- xmlNodePtr xnode;
773
- long line_num;
774
- xnode = rxml_get_xnode(self);
775
-
776
- if (!xmlLineNumbersDefaultValue)
777
- rb_warn(
778
- "Line numbers were not retained: use XML::Parser::default_line_numbers=true");
779
-
780
- line_num = xmlGetLineNo(xnode);
781
- if (line_num == -1)
782
- return (Qnil);
783
- else
784
- return (INT2NUM((long) line_num));
785
- }
786
-
787
- /*
788
- * call-seq:
789
- * node.xlink? -> (true|false)
790
- *
791
- * Determine whether this node is an xlink node.
792
- */
793
- static VALUE rxml_node_xlink_q(VALUE self)
794
- {
795
- xmlNodePtr xnode;
796
- xlinkType xlt;
797
-
798
- xnode = rxml_get_xnode(self);
799
- xlt = xlinkIsLink(xnode->doc, xnode);
800
-
801
- if (xlt == XLINK_TYPE_NONE)
802
- return (Qfalse);
803
- else
804
- return (Qtrue);
805
- }
806
-
807
- /*
808
- * call-seq:
809
- * node.xlink_type -> num
810
- *
811
- * Obtain the type identifier for this xlink, if applicable.
812
- * If this is not an xlink node (see +xlink?+), will return
813
- * nil.
814
- */
815
- static VALUE rxml_node_xlink_type(VALUE self)
816
- {
817
- xmlNodePtr xnode;
818
- xlinkType xlt;
819
-
820
- xnode = rxml_get_xnode(self);
821
- xlt = xlinkIsLink(xnode->doc, xnode);
822
-
823
- if (xlt == XLINK_TYPE_NONE)
824
- return (Qnil);
825
- else
826
- return (INT2NUM(xlt));
827
- }
828
-
829
- /*
830
- * call-seq:
831
- * node.xlink_type_name -> "string"
832
- *
833
- * Obtain the type name for this xlink, if applicable.
834
- * If this is not an xlink node (see +xlink?+), will return
835
- * nil.
836
- */
837
- static VALUE rxml_node_xlink_type_name(VALUE self)
838
- {
839
- xmlNodePtr xnode;
840
- xlinkType xlt;
841
-
842
- xnode = rxml_get_xnode(self);
843
- xlt = xlinkIsLink(xnode->doc, xnode);
844
-
845
- switch (xlt)
846
- {
847
- case XLINK_TYPE_NONE:
848
- return (Qnil);
849
- case XLINK_TYPE_SIMPLE:
850
- return (rxml_new_cstr((const xmlChar*)"simple", NULL));
851
- case XLINK_TYPE_EXTENDED:
852
- return (rxml_new_cstr((const xmlChar*)"extended", NULL));
853
- case XLINK_TYPE_EXTENDED_SET:
854
- return (rxml_new_cstr((const xmlChar*)"extended_set", NULL));
855
- default:
856
- rb_fatal("Unknowng xlink type, %d", xlt);
857
- }
858
- }
859
-
860
- /*
861
- * call-seq:
862
- * node.name -> "string"
863
- *
864
- * Obtain this node's name.
865
- */
866
- static VALUE rxml_node_name_get(VALUE self)
867
- {
868
- xmlNodePtr xnode;
869
- const xmlChar *name;
870
-
871
- xnode = rxml_get_xnode(self);
872
-
873
- switch (xnode->type)
874
- {
875
- case XML_DOCUMENT_NODE:
876
- #ifdef LIBXML_DOCB_ENABLED
877
- case XML_DOCB_DOCUMENT_NODE:
878
- #endif
879
- case XML_HTML_DOCUMENT_NODE:
880
- {
881
- xmlDocPtr doc = (xmlDocPtr) xnode;
882
- name = doc->URL;
883
- break;
884
- }
885
- case XML_ATTRIBUTE_NODE:
886
- {
887
- xmlAttrPtr attr = (xmlAttrPtr) xnode;
888
- name = attr->name;
889
- break;
890
- }
891
- case XML_NAMESPACE_DECL:
892
- {
893
- xmlNsPtr ns = (xmlNsPtr) xnode;
894
- name = ns->prefix;
895
- break;
896
- }
897
- default:
898
- name = xnode->name;
899
- break;
900
- }
901
-
902
- if (xnode->name == NULL)
903
- return (Qnil);
904
- else
905
- return (rxml_new_cstr( name, NULL));
906
- }
907
-
908
- /*
909
- * call-seq:
910
- * node.name = "string"
911
- *
912
- * Set this node's name.
913
- */
914
- static VALUE rxml_node_name_set(VALUE self, VALUE name)
915
- {
916
- xmlNodePtr xnode;
917
- const xmlChar *xname;
918
-
919
- Check_Type(name, T_STRING);
920
- xnode = rxml_get_xnode(self);
921
- xname = (const xmlChar*)StringValuePtr(name);
922
-
923
- /* Note: calling xmlNodeSetName() for a text node is ignored by libXML. */
924
- xmlNodeSetName(xnode, xname);
925
-
926
- return (Qtrue);
927
- }
928
-
929
- /*
930
- * call-seq:
931
- * node.next -> XML::Node
932
- *
933
- * Returns the next sibling node if one exists.
934
- */
935
- static VALUE rxml_node_next_get(VALUE self)
936
- {
937
- xmlNodePtr xnode;
938
-
939
- xnode = rxml_get_xnode(self);
940
-
941
- if (xnode->next)
942
- return (rxml_node_wrap(xnode->next));
943
- else
944
- return (Qnil);
945
- }
946
-
947
- /*
948
- * call-seq:
949
- * curr_node.next = node
950
- *
951
- * Adds the specified node as the next sibling of the current node.
952
- * If the node already exists in the document, it is first removed
953
- * from its existing context. Any adjacent text nodes will be
954
- * merged together, meaning the returned node may be different
955
- * than the original node.
956
- */
957
- static VALUE rxml_node_next_set(VALUE self, VALUE next)
958
- {
959
- return rxml_node_modify_dom(self, next, xmlAddNextSibling);
960
- }
961
-
962
- /*
963
- * call-seq:
964
- * node.parent -> XML::Node
965
- *
966
- * Obtain this node's parent node, if any.
967
- */
968
- static VALUE rxml_node_parent_get(VALUE self)
969
- {
970
- xmlNodePtr xnode;
971
-
972
- xnode = rxml_get_xnode(self);
973
-
974
- if (xnode->parent)
975
- return (rxml_node_wrap(xnode->parent));
976
- else
977
- return (Qnil);
978
- }
979
-
980
- /*
981
- * call-seq:
982
- * node.path -> path
983
- *
984
- * Obtain this node's path.
985
- */
986
- static VALUE rxml_node_path(VALUE self)
987
- {
988
- xmlNodePtr xnode;
989
- xmlChar *path;
990
- VALUE result = Qnil;
991
-
992
- xnode = rxml_get_xnode(self);
993
- path = xmlGetNodePath(xnode);
994
-
995
- if (path)
996
- {
997
- result = rxml_new_cstr( path, NULL);
998
- xmlFree(path);
999
- }
1000
-
1001
- return result;
1002
- }
1003
-
1004
- /*
1005
- * call-seq:
1006
- * node.prev -> XML::Node
1007
- *
1008
- * Obtain the previous sibling, if any.
1009
- */
1010
- static VALUE rxml_node_prev_get(VALUE self)
1011
- {
1012
- xmlNodePtr xnode;
1013
- xmlNodePtr node;
1014
- xnode = rxml_get_xnode(self);
1015
-
1016
- switch (xnode->type)
1017
- {
1018
- case XML_DOCUMENT_NODE:
1019
- #ifdef LIBXML_DOCB_ENABLED
1020
- case XML_DOCB_DOCUMENT_NODE:
1021
- #endif
1022
- case XML_HTML_DOCUMENT_NODE:
1023
- case XML_NAMESPACE_DECL:
1024
- node = NULL;
1025
- break;
1026
- case XML_ATTRIBUTE_NODE:
1027
- {
1028
- xmlAttrPtr attr = (xmlAttrPtr) xnode;
1029
- node = (xmlNodePtr) attr->prev;
1030
- }
1031
- break;
1032
- default:
1033
- node = xnode->prev;
1034
- break;
1035
- }
1036
-
1037
- if (node == NULL)
1038
- return (Qnil);
1039
- else
1040
- return (rxml_node_wrap(node));
1041
- }
1042
-
1043
- /*
1044
- * call-seq:
1045
- * curr_node.prev = node
1046
- *
1047
- * Adds the specified node as the previous sibling of the current node.
1048
- * If the node already exists in the document, it is first removed
1049
- * from its existing context. Any adjacent text nodes will be
1050
- * merged together, meaning the returned node may be different
1051
- * than the original node.
1052
- */
1053
- static VALUE rxml_node_prev_set(VALUE self, VALUE prev)
1054
- {
1055
- return rxml_node_modify_dom(self, prev, xmlAddPrevSibling);
1056
- }
1057
-
1058
- /*
1059
- * call-seq:
1060
- * node.attributes -> attributes
1061
- *
1062
- * Returns the XML::Attributes for this node.
1063
- */
1064
- static VALUE rxml_node_attributes_get(VALUE self)
1065
- {
1066
- xmlNodePtr xnode;
1067
-
1068
- xnode = rxml_get_xnode(self);
1069
- return rxml_attributes_new(xnode);
1070
- }
1071
-
1072
- /*
1073
- * call-seq:
1074
- * node.property("name") -> "string"
1075
- * node["name"] -> "string"
1076
- *
1077
- * Obtain the named property.
1078
- */
1079
- static VALUE rxml_node_attribute_get(VALUE self, VALUE name)
1080
- {
1081
- VALUE attributes = rxml_node_attributes_get(self);
1082
- return rxml_attributes_attribute_get(attributes, name);
1083
- }
1084
-
1085
- /*
1086
- * call-seq:
1087
- * node["name"] = "string"
1088
- *
1089
- * Set the named property.
1090
- */
1091
- static VALUE rxml_node_property_set(VALUE self, VALUE name, VALUE value)
1092
- {
1093
- VALUE attributes = rxml_node_attributes_get(self);
1094
- return rxml_attributes_attribute_set(attributes, name, value);
1095
- }
1096
-
1097
- /*
1098
- * call-seq:
1099
- * node.remove! -> node
1100
- *
1101
- * Removes this node and its children from the document tree by setting its document,
1102
- * parent and siblings to nil. You can add the returned node back into a document.
1103
- * Otherwise, the node will be freed once any references to it go out of scope.
1104
- */
1105
-
1106
- static VALUE rxml_node_remove_ex(VALUE self)
1107
- {
1108
- xmlNodePtr xnode = rxml_get_xnode(self);
1109
-
1110
- // Now unlink the node from its parent
1111
- xmlUnlinkNode(xnode);
1112
-
1113
- // Ruby now manages this node
1114
- rxml_node_manage(xnode, self);
1115
-
1116
- // Now return the removed node so the user can do something with it
1117
- return self;
1118
- }
1119
-
1120
- /*
1121
- * call-seq:
1122
- * curr_node.sibling = node
1123
- *
1124
- * Adds the specified node as the end of the current node's list
1125
- * of siblings. If the node already exists in the document, it
1126
- * is first removed from its existing context. Any adjacent text
1127
- * nodes will be merged together, meaning the returned node may
1128
- * be different than the original node.
1129
- */
1130
- static VALUE rxml_node_sibling_set(VALUE self, VALUE sibling)
1131
- {
1132
- return rxml_node_modify_dom(self, sibling, xmlAddSibling);
1133
- }
1134
-
1135
- /*
1136
- * call-seq:
1137
- * text_node.output_escaping? -> (true|false)
1138
- * element_node.output_escaping? -> (true|false|nil)
1139
- * attribute_node.output_escaping? -> (true|false|nil)
1140
- * other_node.output_escaping? -> (nil)
1141
- *
1142
- * Determine whether this node escapes it's output or not.
1143
- *
1144
- * Text nodes return only +true+ or +false+. Element and attribute nodes
1145
- * examine their immediate text node children to determine the value.
1146
- * Any other type of node always returns +nil+.
1147
- *
1148
- * If an element or attribute node has at least one immediate child text node
1149
- * and all the immediate text node children have the same +output_escaping?+
1150
- * value, that value is returned. Otherwise, +nil+ is returned.
1151
- */
1152
- static VALUE rxml_node_output_escaping_q(VALUE self)
1153
- {
1154
- xmlNodePtr xnode;
1155
- xnode = rxml_get_xnode(self);
1156
-
1157
- switch (xnode->type) {
1158
- case XML_TEXT_NODE:
1159
- return xnode->name==xmlStringTextNoenc ? Qfalse : Qtrue;
1160
- case XML_ELEMENT_NODE:
1161
- case XML_ATTRIBUTE_NODE:
1162
- {
1163
- xmlNodePtr tmp = xnode->children;
1164
- const xmlChar *match = NULL;
1165
-
1166
- /* Find the first text node and use it as the reference. */
1167
- while (tmp && tmp->type != XML_TEXT_NODE)
1168
- tmp = tmp->next;
1169
- if (! tmp)
1170
- return Qnil;
1171
- match = tmp->name;
1172
-
1173
- /* Walk the remaining text nodes until we run out or one doesn't match. */
1174
- while (tmp && (tmp->type != XML_TEXT_NODE || match == tmp->name))
1175
- tmp = tmp->next;
1176
-
1177
- /* We're left with either the mismatched node or the aggregate result. */
1178
- return tmp ? Qnil : (match==xmlStringTextNoenc ? Qfalse : Qtrue);
1179
- }
1180
- break;
1181
- default:
1182
- return Qnil;
1183
- }
1184
- }
1185
-
1186
- /*
1187
- * call-seq:
1188
- * text_node.output_escaping = true|false
1189
- * element_node.output_escaping = true|false
1190
- * attribute_node.output_escaping = true|false
1191
- *
1192
- * Controls whether this text node or the immediate text node children of an
1193
- * element or attribute node escapes their output. Any other type of node
1194
- * will simply ignore this operation.
1195
- *
1196
- * Text nodes which are added to an element or attribute node will be affected
1197
- * by any previous setting of this property.
1198
- */
1199
- static VALUE rxml_node_output_escaping_set(VALUE self, VALUE value)
1200
- {
1201
- xmlNodePtr xnode;
1202
- xnode = rxml_get_xnode(self);
1203
-
1204
- switch (xnode->type) {
1205
- case XML_TEXT_NODE:
1206
- xnode->name = (value != Qfalse && value != Qnil) ? xmlStringText : xmlStringTextNoenc;
1207
- break;
1208
- case XML_ELEMENT_NODE:
1209
- case XML_ATTRIBUTE_NODE:
1210
- {
1211
- const xmlChar *name = (value != Qfalse && value != Qnil) ? xmlStringText : xmlStringTextNoenc;
1212
- xmlNodePtr tmp;
1213
- for (tmp = xnode->children; tmp; tmp = tmp->next)
1214
- if (tmp->type == XML_TEXT_NODE)
1215
- tmp->name = name;
1216
- }
1217
- break;
1218
- default:
1219
- return Qnil;
1220
- }
1221
-
1222
- return (value!=Qfalse && value!=Qnil) ? Qtrue : Qfalse;
1223
- }
1224
-
1225
- /*
1226
- * call-seq:
1227
- * node.space_preserve -> (true|false)
1228
- *
1229
- * Determine whether this node preserves whitespace.
1230
- */
1231
- static VALUE rxml_node_space_preserve_get(VALUE self)
1232
- {
1233
- xmlNodePtr xnode;
1234
-
1235
- xnode = rxml_get_xnode(self);
1236
- return (INT2NUM(xmlNodeGetSpacePreserve(xnode)));
1237
- }
1238
-
1239
- /*
1240
- * call-seq:
1241
- * node.space_preserve = true|false
1242
- *
1243
- * Control whether this node preserves whitespace.
1244
- */
1245
- static VALUE rxml_node_space_preserve_set(VALUE self, VALUE value)
1246
- {
1247
- xmlNodePtr xnode;
1248
- xnode = rxml_get_xnode(self);
1249
-
1250
- if (value == Qfalse)
1251
- xmlNodeSetSpacePreserve(xnode, 0);
1252
- else
1253
- xmlNodeSetSpacePreserve(xnode, 1);
1254
-
1255
- return (Qnil);
1256
- }
1257
-
1258
- /*
1259
- * call-seq:
1260
- * node.type -> num
1261
- *
1262
- * Obtain this node's type identifier.
1263
- */
1264
- static VALUE rxml_node_type(VALUE self)
1265
- {
1266
- xmlNodePtr xnode;
1267
- xnode = rxml_get_xnode(self);
1268
- return (INT2NUM(xnode->type));
1269
- }
1270
-
1271
- /*
1272
- * call-seq:
1273
- * node.copy -> XML::Node
1274
- *
1275
- * Creates a copy of this node. To create a
1276
- * shallow copy set the deep parameter to false.
1277
- * To create a deep copy set the deep parameter
1278
- * to true.
1279
- *
1280
- */
1281
- static VALUE rxml_node_copy(VALUE self, VALUE deep)
1282
- {
1283
- xmlNodePtr xnode;
1284
- xmlNodePtr xcopy;
1285
- int recursive = (deep == Qnil || deep == Qfalse) ? 0 : 1;
1286
- xnode = rxml_get_xnode(self);
1287
-
1288
- xcopy = xmlCopyNode(xnode, recursive);
1289
-
1290
- if (xcopy)
1291
- return rxml_node_wrap(xcopy);
1292
- else
1293
- return Qnil;
1294
- }
1295
-
1296
- void rxml_init_node(void)
1297
- {
1298
- cXMLNode = rb_define_class_under(mXML, "Node", rb_cObject);
1299
-
1300
- rb_define_const(cXMLNode, "SPACE_DEFAULT", INT2NUM(0));
1301
- rb_define_const(cXMLNode, "SPACE_PRESERVE", INT2NUM(1));
1302
- rb_define_const(cXMLNode, "SPACE_NOT_INHERIT", INT2NUM(-1));
1303
- rb_define_const(cXMLNode, "XLINK_ACTUATE_AUTO", INT2NUM(1));
1304
- rb_define_const(cXMLNode, "XLINK_ACTUATE_NONE", INT2NUM(0));
1305
- rb_define_const(cXMLNode, "XLINK_ACTUATE_ONREQUEST", INT2NUM(2));
1306
- rb_define_const(cXMLNode, "XLINK_SHOW_EMBED", INT2NUM(2));
1307
- rb_define_const(cXMLNode, "XLINK_SHOW_NEW", INT2NUM(1));
1308
- rb_define_const(cXMLNode, "XLINK_SHOW_NONE", INT2NUM(0));
1309
- rb_define_const(cXMLNode, "XLINK_SHOW_REPLACE", INT2NUM(3));
1310
- rb_define_const(cXMLNode, "XLINK_TYPE_EXTENDED", INT2NUM(2));
1311
- rb_define_const(cXMLNode, "XLINK_TYPE_EXTENDED_SET", INT2NUM(3));
1312
- rb_define_const(cXMLNode, "XLINK_TYPE_NONE", INT2NUM(0));
1313
- rb_define_const(cXMLNode, "XLINK_TYPE_SIMPLE", INT2NUM(1));
1314
-
1315
- rb_define_const(cXMLNode, "ELEMENT_NODE", INT2FIX(XML_ELEMENT_NODE));
1316
- rb_define_const(cXMLNode, "ATTRIBUTE_NODE", INT2FIX(XML_ATTRIBUTE_NODE));
1317
- rb_define_const(cXMLNode, "TEXT_NODE", INT2FIX(XML_TEXT_NODE));
1318
- rb_define_const(cXMLNode, "CDATA_SECTION_NODE", INT2FIX(XML_CDATA_SECTION_NODE));
1319
- rb_define_const(cXMLNode, "ENTITY_REF_NODE", INT2FIX(XML_ENTITY_REF_NODE));
1320
- rb_define_const(cXMLNode, "ENTITY_NODE", INT2FIX(XML_ENTITY_NODE));
1321
- rb_define_const(cXMLNode, "PI_NODE", INT2FIX(XML_PI_NODE));
1322
- rb_define_const(cXMLNode, "COMMENT_NODE", INT2FIX(XML_COMMENT_NODE));
1323
- rb_define_const(cXMLNode, "DOCUMENT_NODE", INT2FIX(XML_DOCUMENT_NODE));
1324
- rb_define_const(cXMLNode, "DOCUMENT_TYPE_NODE", INT2FIX(XML_DOCUMENT_TYPE_NODE));
1325
- rb_define_const(cXMLNode, "DOCUMENT_FRAG_NODE", INT2FIX(XML_DOCUMENT_FRAG_NODE));
1326
- rb_define_const(cXMLNode, "NOTATION_NODE", INT2FIX(XML_NOTATION_NODE));
1327
- rb_define_const(cXMLNode, "HTML_DOCUMENT_NODE", INT2FIX(XML_HTML_DOCUMENT_NODE));
1328
- rb_define_const(cXMLNode, "DTD_NODE", INT2FIX(XML_DTD_NODE));
1329
- rb_define_const(cXMLNode, "ELEMENT_DECL", INT2FIX(XML_ELEMENT_DECL));
1330
- rb_define_const(cXMLNode, "ATTRIBUTE_DECL", INT2FIX(XML_ATTRIBUTE_DECL));
1331
- rb_define_const(cXMLNode, "ENTITY_DECL", INT2FIX(XML_ENTITY_DECL));
1332
- rb_define_const(cXMLNode, "NAMESPACE_DECL", INT2FIX(XML_NAMESPACE_DECL));
1333
- rb_define_const(cXMLNode, "XINCLUDE_START", INT2FIX(XML_XINCLUDE_START));
1334
- rb_define_const(cXMLNode, "XINCLUDE_END", INT2FIX(XML_XINCLUDE_END));
1335
-
1336
- #ifdef LIBXML_DOCB_ENABLED
1337
- rb_define_const(cXMLNode, "DOCB_DOCUMENT_NODE", INT2FIX(XML_DOCB_DOCUMENT_NODE));
1338
- #else
1339
- rb_define_const(cXMLNode, "DOCB_DOCUMENT_NODE", Qnil);
1340
- #endif
1341
-
1342
- rb_define_singleton_method(cXMLNode, "new_cdata", rxml_node_new_cdata, -1);
1343
- rb_define_singleton_method(cXMLNode, "new_comment", rxml_node_new_comment, -1);
1344
- rb_define_singleton_method(cXMLNode, "new_pi", rxml_node_new_pi, -1);
1345
- rb_define_singleton_method(cXMLNode, "new_text", rxml_node_new_text, 1);
1346
-
1347
- /* Initialization */
1348
- rb_define_alloc_func(cXMLNode, rxml_node_alloc);
1349
- rb_define_method(cXMLNode, "initialize", rxml_node_initialize, -1);
1350
-
1351
- /* Traversal */
1352
- rb_include_module(cXMLNode, rb_mEnumerable);
1353
- rb_define_method(cXMLNode, "[]", rxml_node_attribute_get, 1);
1354
- rb_define_method(cXMLNode, "each", rxml_node_each, 0);
1355
- rb_define_method(cXMLNode, "first", rxml_node_first_get, 0);
1356
- rb_define_method(cXMLNode, "last", rxml_node_last_get, 0);
1357
- rb_define_method(cXMLNode, "next", rxml_node_next_get, 0);
1358
- rb_define_method(cXMLNode, "parent", rxml_node_parent_get, 0);
1359
- rb_define_method(cXMLNode, "prev", rxml_node_prev_get, 0);
1360
-
1361
- /* Modification */
1362
- rb_define_method(cXMLNode, "[]=", rxml_node_property_set, 2);
1363
- rb_define_method(cXMLNode, "<<", rxml_node_content_add, 1);
1364
- rb_define_method(cXMLNode, "sibling=", rxml_node_sibling_set, 1);
1365
- rb_define_method(cXMLNode, "next=", rxml_node_next_set, 1);
1366
- rb_define_method(cXMLNode, "prev=", rxml_node_prev_set, 1);
1367
-
1368
- /* Rest of the node api */
1369
- rb_define_method(cXMLNode, "attributes", rxml_node_attributes_get, 0);
1370
- rb_define_method(cXMLNode, "base_uri", rxml_node_base_uri_get, 0);
1371
- rb_define_method(cXMLNode, "base_uri=", rxml_node_base_uri_set, 1);
1372
- rb_define_method(cXMLNode, "blank?", rxml_node_empty_q, 0);
1373
- rb_define_method(cXMLNode, "copy", rxml_node_copy, 1);
1374
- rb_define_method(cXMLNode, "content", rxml_node_content_get, 0);
1375
- rb_define_method(cXMLNode, "content=", rxml_node_content_set, 1);
1376
- rb_define_method(cXMLNode, "debug", rxml_node_debug, 0);
1377
- rb_define_method(cXMLNode, "doc", rxml_node_doc, 0);
1378
- rb_define_method(cXMLNode, "empty?", rxml_node_empty_q, 0);
1379
- rb_define_method(cXMLNode, "eql?", rxml_node_eql_q, 1);
1380
- rb_define_method(cXMLNode, "lang", rxml_node_lang_get, 0);
1381
- rb_define_method(cXMLNode, "lang=", rxml_node_lang_set, 1);
1382
- rb_define_method(cXMLNode, "line_num", rxml_node_line_num, 0);
1383
- rb_define_method(cXMLNode, "name", rxml_node_name_get, 0);
1384
- rb_define_method(cXMLNode, "name=", rxml_node_name_set, 1);
1385
- rb_define_method(cXMLNode, "node_type", rxml_node_type, 0);
1386
- rb_define_method(cXMLNode, "output_escaping?", rxml_node_output_escaping_q, 0);
1387
- rb_define_method(cXMLNode, "output_escaping=", rxml_node_output_escaping_set, 1);
1388
- rb_define_method(cXMLNode, "path", rxml_node_path, 0);
1389
- rb_define_method(cXMLNode, "remove!", rxml_node_remove_ex, 0);
1390
- rb_define_method(cXMLNode, "space_preserve", rxml_node_space_preserve_get, 0);
1391
- rb_define_method(cXMLNode, "space_preserve=", rxml_node_space_preserve_set, 1);
1392
- rb_define_method(cXMLNode, "to_s", rxml_node_to_s, -1);
1393
- rb_define_method(cXMLNode, "xlink?", rxml_node_xlink_q, 0);
1394
- rb_define_method(cXMLNode, "xlink_type", rxml_node_xlink_type, 0);
1395
- rb_define_method(cXMLNode, "xlink_type_name", rxml_node_xlink_type_name, 0);
1396
-
1397
- rb_define_alias(cXMLNode, "==", "eql?");
1398
- }
1
+ #include "ruby_libxml.h"
2
+ #include "ruby_xml_node.h"
3
+ #include <assert.h>
4
+
5
+ #include <libxml/debugXML.h>
6
+ #include <libxml/parserInternals.h>
7
+ #include <libxml/xlink.h>
8
+
9
+ VALUE cXMLNode;
10
+
11
+ #ifndef TYPED_DATA_EMBEDDED
12
+ #define TYPED_DATA_EMBEDDED ((VALUE)1)
13
+ #endif
14
+
15
+ /* Document-class: LibXML::XML::Node
16
+ *
17
+ * Nodes are the primary objects that make up an XML document.
18
+ * The node class represents most node types that are found in
19
+ * an XML document (but not LibXML::XML::Attributes, see LibXML::XML::Attr).
20
+ * It exposes libxml's full API for creating, querying
21
+ * moving and deleting node objects. Many of these methods are
22
+ * documented in the DOM Level 3 specification found at:
23
+ * http://www.w3.org/TR/DOM-Level-3-Core/. */
24
+
25
+
26
+ /* Memory management:
27
+ *
28
+ * The bindings create a one-to-one mapping between ruby objects and
29
+ * libxml documents and libxml parent nodes (ie, nodes that do not
30
+ * have a parent and do not belong to a document). In these cases,
31
+ * the bindings manage the memory. They do this by installing a free
32
+ * function and storing a back pointer to the Ruby object from the xmlnode
33
+ * using a pointer-keyed registry (see ruby_xml_registry.c). When the
34
+ * Ruby object goes out of scope, the underlying libxml structure is freed.
35
+ * Libxml itself then frees all child node (recursively).
36
+ *
37
+ * For all other nodes (the vast majority), the bindings create temporary
38
+ * Ruby objects that get freed once they go out of scope. Thus there can be
39
+ * more than one ruby object pointing to the same xml node. To mostly hide
40
+ * this from programmers on the ruby side, the #eql? and #== methods are
41
+ * overriden to check if two ruby objects wrap the same xmlnode. If they do,
42
+ * then the methods return true. During the mark phase, each of these temporary
43
+ * objects marks its owning document, thereby keeping the Ruby document object
44
+ * alive and thus the xmldoc tree.
45
+ *
46
+ * In the sweep phase of the garbage collector, or when a program ends,
47
+ * there is no order to how Ruby objects are freed. In fact, the ruby document
48
+ * object is almost always freed before any ruby objects that wrap child nodes.
49
+ * However, this is ok because those ruby objects do not have a free function
50
+ * and are no longer in scope (since if they were the document would not be freed).
51
+ */
52
+
53
+ static void rxml_node_free(void *data)
54
+ {
55
+ xmlNodePtr xnode = (xmlNodePtr)data;
56
+ if (!xnode) return;
57
+
58
+ /* The ruby object wrapping the xml object no longer exists and this
59
+ is a standalone node without a document or parent so ruby is
60
+ responsible for freeing the underlying node.*/
61
+ if (xnode->doc == NULL && xnode->parent == NULL)
62
+ {
63
+ rxml_registry_unregister(xnode);
64
+ xmlFreeNode(xnode);
65
+ }
66
+ }
67
+
68
+ /* Three TypedData types are used for nodes, all sharing rxml_node_data_type
69
+ * as the parent type so that TypedData_Get_Struct type checks pass for any
70
+ * variant:
71
+ *
72
+ * rxml_node_data_type - Default. Marks the owning document to prevent
73
+ * GC from collecting it. No free function since
74
+ * libxml owns the node memory.
75
+ *
76
+ * rxml_node_unmanaged_data_type - No mark, no free. Used for transient nodes
77
+ * whose lifetime is not controlled by Ruby (e.g.
78
+ * nodes returned by xmlTextReaderExpand that are
79
+ * freed on the next reader read call).
80
+ *
81
+ * rxml_node_managed_data_type - Marks the document and frees standalone nodes
82
+ * (no doc, no parent) when the Ruby wrapper is
83
+ * collected. Used for nodes created from Ruby
84
+ * that have not yet been inserted into a document.
85
+ */
86
+ const rb_data_type_t rxml_node_data_type = {
87
+ .wrap_struct_name = "LibXML::XML::Node",
88
+ .function = { .dmark = (RUBY_DATA_FUNC)rxml_node_mark, .dfree = NULL },
89
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
90
+ };
91
+
92
+ const rb_data_type_t rxml_node_unmanaged_data_type = {
93
+ .wrap_struct_name = "LibXML::XML::Node (unmanaged)",
94
+ .function = { .dmark = NULL, .dfree = NULL },
95
+ .parent = &rxml_node_data_type,
96
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
97
+ };
98
+
99
+ static const rb_data_type_t rxml_node_managed_data_type = {
100
+ .wrap_struct_name = "LibXML::XML::Node (managed)",
101
+ .function = { .dmark = (RUBY_DATA_FUNC)rxml_node_mark, .dfree = rxml_node_free },
102
+ .parent = &rxml_node_data_type,
103
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
104
+ };
105
+
106
+ void rxml_node_manage(xmlNodePtr xnode, VALUE node)
107
+ {
108
+ VALUE *type_ptr = (VALUE *)&RTYPEDDATA(node)->type;
109
+ *type_ptr = (*type_ptr & TYPED_DATA_EMBEDDED) | (VALUE)&rxml_node_managed_data_type;
110
+ rxml_registry_register(xnode, node);
111
+ }
112
+
113
+ void rxml_node_unmanage(xmlNodePtr xnode, VALUE node)
114
+ {
115
+ VALUE *type_ptr = (VALUE *)&RTYPEDDATA(node)->type;
116
+ *type_ptr = (*type_ptr & TYPED_DATA_EMBEDDED) | (VALUE)&rxml_node_data_type;
117
+ rxml_registry_unregister(xnode);
118
+ }
119
+
120
+ xmlNodePtr rxml_node_root(xmlNodePtr xnode)
121
+ {
122
+ xmlNodePtr current = xnode;
123
+
124
+ while (current->parent)
125
+ {
126
+ current = current->parent;
127
+ }
128
+
129
+ return current;
130
+ }
131
+
132
+ void rxml_node_mark(xmlNodePtr xnode)
133
+ {
134
+ if (xnode->doc)
135
+ {
136
+ VALUE doc = rxml_registry_lookup(xnode->doc);
137
+ if (!NIL_P(doc))
138
+ rb_gc_mark(doc);
139
+ }
140
+ else if (xnode->parent)
141
+ {
142
+ xmlNodePtr root = rxml_node_root(xnode);
143
+ VALUE node = rxml_registry_lookup(root);
144
+ if (!NIL_P(node))
145
+ rb_gc_mark(node);
146
+ }
147
+ }
148
+
149
+ VALUE rxml_node_wrap(xmlNodePtr xnode)
150
+ {
151
+ VALUE result = Qnil;
152
+
153
+ // Is this node already wrapped?
154
+ result = rxml_registry_lookup(xnode);
155
+ if (NIL_P(result))
156
+ {
157
+ result = TypedData_Wrap_Struct(cXMLNode, &rxml_node_data_type, xnode);
158
+ }
159
+
160
+ if (!xnode->doc && !xnode->parent)
161
+ {
162
+ rxml_node_manage(xnode, result);
163
+ }
164
+ return result;
165
+ }
166
+
167
+ static VALUE rxml_node_alloc(VALUE klass)
168
+ {
169
+ // This node was created from Ruby so we are responsible for freeing it not libxml
170
+ return TypedData_Wrap_Struct(klass, &rxml_node_data_type, NULL);
171
+ }
172
+
173
+ static xmlNodePtr rxml_get_xnode(VALUE node)
174
+ {
175
+ xmlNodePtr result;
176
+ TypedData_Get_Struct(node, xmlNode, &rxml_node_data_type, result);
177
+
178
+ if (!result)
179
+ rb_raise(rb_eRuntimeError, "This node has already been freed.");
180
+
181
+ return result;
182
+ }
183
+
184
+ /*
185
+ * call-seq:
186
+ * XML::Node.new_cdata(content = nil) -> XML::Node
187
+ *
188
+ * Create a new #CDATA node, optionally setting
189
+ * the node's content.
190
+ */
191
+ static VALUE rxml_node_new_cdata(int argc, VALUE *argv, VALUE klass)
192
+ {
193
+ VALUE content = Qnil;
194
+ xmlNodePtr xnode;
195
+
196
+ rb_scan_args(argc, argv, "01", &content);
197
+
198
+ if (NIL_P(content))
199
+ {
200
+ xnode = xmlNewCDataBlock(NULL, NULL, 0);
201
+ }
202
+ else
203
+ {
204
+ content = rb_obj_as_string(content);
205
+ xnode = xmlNewCDataBlock(NULL, (xmlChar*) StringValuePtr(content), (int)RSTRING_LEN(content));
206
+ }
207
+
208
+ if (xnode == NULL)
209
+ rxml_raise(xmlGetLastError());
210
+
211
+ return rxml_node_wrap(xnode);
212
+ }
213
+
214
+ /*
215
+ * call-seq:
216
+ * XML::Node.new_comment(content = nil) -> XML::Node
217
+ *
218
+ * Create a new comment node, optionally setting
219
+ * the node's content.
220
+ *
221
+ */
222
+ static VALUE rxml_node_new_comment(int argc, VALUE *argv, VALUE klass)
223
+ {
224
+ VALUE content = Qnil;
225
+ xmlNodePtr xnode;
226
+
227
+ rb_scan_args(argc, argv, "01", &content);
228
+
229
+ if (NIL_P(content))
230
+ {
231
+ xnode = xmlNewComment(NULL);
232
+ }
233
+ else
234
+ {
235
+ content = rb_obj_as_string(content);
236
+ xnode = xmlNewComment((xmlChar*) StringValueCStr(content));
237
+ }
238
+
239
+ if (xnode == NULL)
240
+ rxml_raise(xmlGetLastError());
241
+
242
+ return rxml_node_wrap(xnode);
243
+ }
244
+
245
+ /*
246
+ * call-seq:
247
+ * XML::Node.new_pi(name, content = nil) -> XML::Node
248
+ *
249
+ * Create a new pi node, optionally setting
250
+ * the node's content.
251
+ *
252
+ */
253
+ static VALUE rxml_node_new_pi(int argc, VALUE *argv, VALUE klass)
254
+ {
255
+ VALUE name = Qnil;
256
+ VALUE content = Qnil;
257
+ xmlNodePtr xnode;
258
+
259
+ rb_scan_args(argc, argv, "11", &name, &content);
260
+
261
+ if (NIL_P(name))
262
+ {
263
+ rb_raise(rb_eRuntimeError, "You must provide me with a name for a PI.");
264
+ }
265
+ name = rb_obj_as_string(name);
266
+ if (NIL_P(content))
267
+ {
268
+ xnode = xmlNewPI((xmlChar*) StringValuePtr(name), NULL);
269
+ }
270
+ else
271
+ {
272
+ content = rb_obj_as_string(content);
273
+ xnode = xmlNewPI((xmlChar*) StringValuePtr(name), (xmlChar*) StringValueCStr(content));
274
+ }
275
+
276
+ if (xnode == NULL)
277
+ rxml_raise(xmlGetLastError());
278
+
279
+ return rxml_node_wrap(xnode);
280
+ }
281
+
282
+ /*
283
+ * call-seq:
284
+ * XML::Node.new_text(content) -> XML::Node
285
+ *
286
+ * Create a new text node.
287
+ *
288
+ */
289
+ static VALUE rxml_node_new_text(VALUE klass, VALUE content)
290
+ {
291
+ xmlNodePtr xnode;
292
+ Check_Type(content, T_STRING);
293
+ content = rb_obj_as_string(content);
294
+
295
+ xnode = xmlNewText((xmlChar*) StringValueCStr(content));
296
+
297
+ if (xnode == NULL)
298
+ rxml_raise(xmlGetLastError());
299
+
300
+ return rxml_node_wrap(xnode);
301
+ }
302
+
303
+ static VALUE rxml_node_content_set(VALUE self, VALUE content);
304
+
305
+ /*
306
+ * call-seq:
307
+ * XML::Node.initialize(name, content = nil, namespace = nil) -> XML::Node
308
+ *
309
+ * Creates a new element with the specified name, content and
310
+ * namespace. The content and namespace may be nil.
311
+ */
312
+ static VALUE rxml_node_initialize(int argc, VALUE *argv, VALUE self)
313
+ {
314
+ VALUE name;
315
+ VALUE content;
316
+ VALUE ns;
317
+ xmlNodePtr xnode = NULL;
318
+ xmlNsPtr xns = NULL;
319
+
320
+ rb_scan_args(argc, argv, "12", &name, &content, &ns);
321
+
322
+ name = rb_obj_as_string(name);
323
+
324
+ if (!NIL_P(ns))
325
+ TypedData_Get_Struct(ns, xmlNs, &rxml_namespace_type, xns);
326
+
327
+ xnode = xmlNewNode(xns, (xmlChar*) StringValuePtr(name));
328
+
329
+ if (xnode == NULL)
330
+ rxml_raise(xmlGetLastError());
331
+
332
+ // Link the ruby wrapper to the underlying libxml node
333
+ RTYPEDDATA_DATA(self) = xnode;
334
+
335
+ // Ruby is in charge of managing this node's memory
336
+ rxml_node_manage(xnode, self);
337
+
338
+ if (!NIL_P(content))
339
+ rxml_node_content_set(self, content);
340
+
341
+ return self;
342
+ }
343
+
344
+ static VALUE rxml_node_modify_dom(VALUE self, VALUE target,
345
+ xmlNodePtr (*xmlFunc)(xmlNodePtr, xmlNodePtr))
346
+ {
347
+ xmlNodePtr xnode, xtarget, xresult;
348
+
349
+ if (rb_obj_is_kind_of(target, cXMLNode) == Qfalse)
350
+ rb_raise(rb_eTypeError, "Must pass an XML::Node object");
351
+
352
+ xnode = rxml_get_xnode(self);
353
+ xtarget = rxml_get_xnode(target);
354
+
355
+ if (xtarget->doc != NULL && xtarget->doc != xnode->doc)
356
+ rb_raise(eXMLError, "Nodes belong to different documents. You must first import the node by calling LibXML::XML::Document.import");
357
+
358
+ xmlUnlinkNode(xtarget);
359
+
360
+ // Target is about to have a parent, so stop having ruby manage it.
361
+ rxml_node_unmanage(xtarget, target);
362
+
363
+ // This target node could be freed here and be replaced by a different node
364
+ xresult = xmlFunc(xnode, xtarget);
365
+
366
+ if (!xresult)
367
+ rxml_raise(xmlGetLastError());
368
+
369
+ /* Assume the target was freed, we need to fix up the ruby object to point to the
370
+ newly returned node. */
371
+ RTYPEDDATA_DATA(target) = xresult;
372
+
373
+ return target;
374
+ }
375
+
376
+ /*
377
+ * call-seq:
378
+ * node.base_uri -> "uri"
379
+ *
380
+ * Obtain this node's base URI.
381
+ */
382
+ static VALUE rxml_node_base_uri_get(VALUE self)
383
+ {
384
+ xmlNodePtr xnode;
385
+ xmlChar* base_uri;
386
+ VALUE result = Qnil;
387
+
388
+ xnode = rxml_get_xnode(self);
389
+
390
+ if (xnode->doc == NULL)
391
+ return (result);
392
+
393
+ base_uri = xmlNodeGetBase(xnode->doc, xnode);
394
+ if (base_uri)
395
+ {
396
+ result = rxml_new_cstr( base_uri, NULL);
397
+ xmlFree(base_uri);
398
+ }
399
+
400
+ return (result);
401
+ }
402
+
403
+ // TODO node_base_set should support setting back to nil
404
+
405
+ /*
406
+ * call-seq:
407
+ * node.base_uri = "uri"
408
+ *
409
+ * Set this node's base URI.
410
+ */
411
+ static VALUE rxml_node_base_uri_set(VALUE self, VALUE uri)
412
+ {
413
+ xmlNodePtr xnode;
414
+
415
+ Check_Type(uri, T_STRING);
416
+ xnode = rxml_get_xnode(self);
417
+ if (xnode->doc == NULL)
418
+ return (Qnil);
419
+
420
+ xmlNodeSetBase(xnode, (xmlChar*) StringValuePtr(uri));
421
+ return (Qtrue);
422
+ }
423
+
424
+ /*
425
+ * call-seq:
426
+ * node.content -> "string"
427
+ *
428
+ * Obtain this node's content as a string.
429
+ */
430
+ static VALUE rxml_node_content_get(VALUE self)
431
+ {
432
+ xmlNodePtr xnode;
433
+ xmlChar *content;
434
+ VALUE result = Qnil;
435
+
436
+ xnode = rxml_get_xnode(self);
437
+ content = xmlNodeGetContent(xnode);
438
+ if (content)
439
+ {
440
+ result = rxml_new_cstr(content, NULL);
441
+ xmlFree(content);
442
+ }
443
+
444
+ return result;
445
+ }
446
+
447
+ /*
448
+ * call-seq:
449
+ * node.content = "string"
450
+ *
451
+ * Set this node's content to the specified string.
452
+ */
453
+ static VALUE rxml_node_content_set(VALUE self, VALUE content)
454
+ {
455
+ xmlNodePtr xnode;
456
+ xmlChar* encoded_content;
457
+
458
+ Check_Type(content, T_STRING);
459
+ xnode = rxml_get_xnode(self);
460
+ encoded_content = xmlEncodeSpecialChars(xnode->doc, (xmlChar*) StringValuePtr(content));
461
+ xmlNodeSetContent(xnode, encoded_content);
462
+ xmlFree(encoded_content);
463
+ return (Qtrue);
464
+ }
465
+
466
+ /*
467
+ * call-seq:
468
+ * node.debug -> true|false
469
+ *
470
+ * Print libxml debugging information to stdout.
471
+ * Requires that libxml was compiled with debugging enabled.
472
+ */
473
+ static VALUE rxml_node_debug(VALUE self)
474
+ {
475
+ #ifdef LIBXML_DEBUG_ENABLED
476
+ xmlNodePtr xnode;
477
+ xnode = rxml_get_xnode(self);
478
+ xmlDebugDumpNode(NULL, xnode, 2);
479
+ return Qtrue;
480
+ #else
481
+ rb_warn("libxml was compiled without debugging support.");
482
+ return Qfalse;
483
+ #endif
484
+ }
485
+
486
+ /*
487
+ * call-seq:
488
+ * node.first -> XML::Node
489
+ *
490
+ * Returns this node's first child node if any.
491
+ */
492
+ static VALUE rxml_node_first_get(VALUE self)
493
+ {
494
+ xmlNodePtr xnode;
495
+
496
+ xnode = rxml_get_xnode(self);
497
+
498
+ if (xnode->children)
499
+ return (rxml_node_wrap(xnode->children));
500
+ else
501
+ return (Qnil);
502
+ }
503
+
504
+
505
+ /*
506
+ * call-seq:
507
+ * curr_node << "Some text"
508
+ * curr_node << node
509
+ *
510
+ * Add the specified text or XML::Node as a new child node to the
511
+ * current node.
512
+ *
513
+ * If the specified argument is a string, it should be a raw string
514
+ * that contains unescaped XML special characters. Entity references
515
+ * are not supported.
516
+ *
517
+ * The method will return the current node.
518
+ */
519
+ static VALUE rxml_node_content_add(VALUE self, VALUE obj)
520
+ {
521
+ xmlNodePtr xnode;
522
+ VALUE str;
523
+
524
+ xnode = rxml_get_xnode(self);
525
+
526
+ /* XXX This should only be legal for a CDATA type node, I think,
527
+ * resulting in a merge of content, as if a string were passed
528
+ * danj 070827
529
+ */
530
+ if (rb_obj_is_kind_of(obj, cXMLNode))
531
+ {
532
+ rxml_node_modify_dom(self, obj, xmlAddChild);
533
+ }
534
+ else
535
+ {
536
+ str = rb_obj_as_string(obj);
537
+ if (NIL_P(str) || TYPE(str) != T_STRING)
538
+ rb_raise(rb_eTypeError, "invalid argument: must be string or XML::Node");
539
+
540
+ xmlNodeAddContent(xnode, (xmlChar*) StringValuePtr(str));
541
+ }
542
+ return self;
543
+ }
544
+
545
+ /*
546
+ * call-seq:
547
+ * node.doc -> document
548
+ *
549
+ * Obtain the XML::Document this node belongs to.
550
+ */
551
+ static VALUE rxml_node_doc(VALUE self)
552
+ {
553
+ xmlDocPtr xdoc = NULL;
554
+ xmlNodePtr xnode = rxml_get_xnode(self);
555
+
556
+ switch (xnode->type)
557
+ {
558
+ case XML_DOCUMENT_NODE:
559
+ #ifdef LIBXML_DOCB_ENABLED
560
+ case XML_DOCB_DOCUMENT_NODE:
561
+ #endif
562
+ case XML_HTML_DOCUMENT_NODE:
563
+ case XML_NAMESPACE_DECL:
564
+ break;
565
+ case XML_ATTRIBUTE_NODE:
566
+ xdoc = (xmlDocPtr)((xmlAttrPtr) xnode->doc);
567
+ break;
568
+ default:
569
+ xdoc = xnode->doc;
570
+ }
571
+
572
+ if (xdoc == NULL)
573
+ return (Qnil);
574
+
575
+ return rxml_registry_lookup(xdoc);
576
+ }
577
+
578
+ /*
579
+ * call-seq:
580
+ * node.to_s -> "string"
581
+ * node.to_s(:indent => true, :encoding => 'UTF-8', :level => 0) -> "string"
582
+ *
583
+ * Converts a node, and all of its children, to a string representation.
584
+ * To include only the node's children, use the the XML::Node#inner_xml
585
+ * method.
586
+ *
587
+ * You may provide an optional hash table to control how the string is
588
+ * generated. Valid options are:
589
+ *
590
+ * :indent - Specifies if the string should be indented. The default value
591
+ * is true. Note that indentation is only added if both :indent is
592
+ * true and XML.indent_tree_output is true. If :indent is set to false,
593
+ * then both indentation and line feeds are removed from the result.
594
+ *
595
+ * :level - Specifies the indentation level. The amount of indentation
596
+ * is equal to the (level * number_spaces) + number_spaces, where libxml
597
+ * defaults the number of spaces to 2. Thus a level of 0 results in
598
+ * 2 spaces, level 1 results in 4 spaces, level 2 results in 6 spaces, etc.
599
+ *
600
+ * :encoding - Specifies the output encoding of the string. It
601
+ * defaults to XML::Encoding::UTF8. To change it, use one of the
602
+ * XML::Encoding encoding constants. */
603
+
604
+ static VALUE rxml_node_to_s(int argc, VALUE *argv, VALUE self)
605
+ {
606
+ VALUE result = Qnil;
607
+ VALUE options = Qnil;
608
+ xmlNodePtr xnode;
609
+ xmlCharEncodingHandlerPtr encodingHandler;
610
+ xmlOutputBufferPtr output;
611
+
612
+ int level = 0;
613
+ int indent = 1;
614
+ const xmlChar *xencoding = (const xmlChar*)"UTF-8";
615
+
616
+ rb_scan_args(argc, argv, "01", &options);
617
+
618
+ if (!NIL_P(options))
619
+ {
620
+ VALUE rencoding, rindent, rlevel;
621
+ Check_Type(options, T_HASH);
622
+ rencoding = rb_hash_aref(options, ID2SYM(rb_intern("encoding")));
623
+ rindent = rb_hash_aref(options, ID2SYM(rb_intern("indent")));
624
+ rlevel = rb_hash_aref(options, ID2SYM(rb_intern("level")));
625
+
626
+ if (rindent == Qfalse)
627
+ indent = 0;
628
+
629
+ if (rlevel != Qnil)
630
+ level = NUM2INT(rlevel);
631
+
632
+ if (rencoding != Qnil)
633
+ {
634
+ xencoding = (const xmlChar*)xmlGetCharEncodingName((xmlCharEncoding)NUM2INT(rencoding));
635
+ if (!xencoding)
636
+ rb_raise(rb_eArgError, "Unknown encoding value: %d", NUM2INT(rencoding));
637
+ }
638
+ }
639
+
640
+ encodingHandler = xmlFindCharEncodingHandler((const char*)xencoding);
641
+ output = xmlAllocOutputBuffer(encodingHandler);
642
+
643
+ xnode = rxml_get_xnode(self);
644
+
645
+ xmlNodeDumpOutput(output, xnode->doc, xnode, level, indent, (const char*)xencoding);
646
+ xmlOutputBufferFlush(output);
647
+
648
+ #ifdef LIBXML2_NEW_BUFFER
649
+ if (output->conv)
650
+ result = rxml_new_cstr(xmlBufContent(output->conv), xencoding);
651
+ else
652
+ result = rxml_new_cstr(xmlBufContent(output->buffer), xencoding);
653
+ #else
654
+ if (output->conv)
655
+ result = rxml_new_cstr(xmlBufferContent(output->conv), xencoding);
656
+ else
657
+ result = rxml_new_cstr(xmlBufferContent(output->buffer), xencoding);
658
+ #endif
659
+
660
+ xmlOutputBufferClose(output);
661
+
662
+ return result;
663
+ }
664
+
665
+
666
+ /*
667
+ * call-seq:
668
+ * node.each -> XML::Node
669
+ *
670
+ * Iterates over this node's children, including text
671
+ * nodes, element nodes, etc. If you wish to iterate
672
+ * only over child elements, use XML::Node#each_element.
673
+ *
674
+ * doc = XML::Document.new('model/books.xml')
675
+ * doc.root.each {|node| puts node}
676
+ */
677
+ static VALUE rxml_node_each(VALUE self)
678
+ {
679
+ xmlNodePtr xnode;
680
+ xmlNodePtr xcurrent;
681
+ xnode = rxml_get_xnode(self);
682
+
683
+ xcurrent = xnode->children;
684
+
685
+ while (xcurrent)
686
+ {
687
+ /* The user could remove this node, so first stache
688
+ away the next node. */
689
+ xmlNodePtr xnext = xcurrent->next;
690
+
691
+ rb_yield(rxml_node_wrap(xcurrent));
692
+ xcurrent = xnext;
693
+ }
694
+ return Qnil;
695
+ }
696
+
697
+ /*
698
+ * call-seq:
699
+ * node.empty? -> (true|false)
700
+ *
701
+ * Determine whether this node is an empty or whitespace only text-node.
702
+ */
703
+ static VALUE rxml_node_empty_q(VALUE self)
704
+ {
705
+ xmlNodePtr xnode;
706
+ xnode = rxml_get_xnode(self);
707
+ if (xnode == NULL)
708
+ return (Qnil);
709
+
710
+ return ((xmlIsBlankNode(xnode) == 1) ? Qtrue : Qfalse);
711
+ }
712
+
713
+
714
+ /*
715
+ * call-seq:
716
+ * node.eql?(other_node) => (true|false)
717
+ *
718
+ * Test equality between the two nodes. Two nodes are equal
719
+ * if they are the same node.*/
720
+ static VALUE rxml_node_eql_q(VALUE self, VALUE other)
721
+ {
722
+ if (self == other)
723
+ {
724
+ return Qtrue;
725
+ }
726
+ else if (NIL_P(other))
727
+ {
728
+ return Qfalse;
729
+ }
730
+ else
731
+ {
732
+ xmlNodePtr xnode = rxml_get_xnode(self);
733
+ xmlNodePtr xnode_other = rxml_get_xnode(other);
734
+ return xnode == xnode_other ? Qtrue : Qfalse;
735
+ }
736
+ }
737
+
738
+ /*
739
+ * call-seq:
740
+ * node.lang -> "string"
741
+ *
742
+ * Obtain the language set for this node, if any.
743
+ * This is set in XML via the xml:lang attribute.
744
+ */
745
+ static VALUE rxml_node_lang_get(VALUE self)
746
+ {
747
+ xmlNodePtr xnode;
748
+ xmlChar *lang;
749
+ VALUE result = Qnil;
750
+
751
+ xnode = rxml_get_xnode(self);
752
+ lang = xmlNodeGetLang(xnode);
753
+
754
+ if (lang)
755
+ {
756
+ result = rxml_new_cstr( lang, NULL);
757
+ xmlFree(lang);
758
+ }
759
+
760
+ return (result);
761
+ }
762
+
763
+ // TODO node_lang_set should support setting back to nil
764
+
765
+ /*
766
+ * call-seq:
767
+ * node.lang = "string"
768
+ *
769
+ * Set the language for this node. This affects the value
770
+ * of the xml:lang attribute.
771
+ */
772
+ static VALUE rxml_node_lang_set(VALUE self, VALUE lang)
773
+ {
774
+ xmlNodePtr xnode;
775
+
776
+ Check_Type(lang, T_STRING);
777
+ xnode = rxml_get_xnode(self);
778
+ xmlNodeSetLang(xnode, (xmlChar*) StringValuePtr(lang));
779
+
780
+ return (Qtrue);
781
+ }
782
+
783
+ /*
784
+ * call-seq:
785
+ * node.last -> XML::Node
786
+ *
787
+ * Obtain the last child node of this node, if any.
788
+ */
789
+ static VALUE rxml_node_last_get(VALUE self)
790
+ {
791
+ xmlNodePtr xnode;
792
+
793
+ xnode = rxml_get_xnode(self);
794
+
795
+ if (xnode->last)
796
+ return (rxml_node_wrap(xnode->last));
797
+ else
798
+ return (Qnil);
799
+ }
800
+
801
+ /*
802
+ * call-seq:
803
+ * node.line_num -> num
804
+ *
805
+ * Obtain the line number (in the XML document) that this
806
+ * node was read from. If +default_line_numbers+ is set
807
+ * false (the default), this method returns zero.
808
+ */
809
+ static VALUE rxml_node_line_num(VALUE self)
810
+ {
811
+ xmlNodePtr xnode;
812
+ long line_num;
813
+ xnode = rxml_get_xnode(self);
814
+
815
+ line_num = xmlGetLineNo(xnode);
816
+ if (line_num == -1)
817
+ return (Qnil);
818
+ else
819
+ return (LONG2NUM((long) line_num));
820
+ }
821
+
822
+ /*
823
+ * call-seq:
824
+ * node.xlink? -> (true|false)
825
+ *
826
+ * Determine whether this node is an xlink node.
827
+ */
828
+ static VALUE rxml_node_xlink_q(VALUE self)
829
+ {
830
+ xmlNodePtr xnode;
831
+ xlinkType xlt;
832
+
833
+ xnode = rxml_get_xnode(self);
834
+ xlt = xlinkIsLink(xnode->doc, xnode);
835
+
836
+ if (xlt == XLINK_TYPE_NONE)
837
+ return (Qfalse);
838
+ else
839
+ return (Qtrue);
840
+ }
841
+
842
+ /*
843
+ * call-seq:
844
+ * node.xlink_type -> num
845
+ *
846
+ * Obtain the type identifier for this xlink, if applicable.
847
+ * If this is not an xlink node (see +xlink?+), will return
848
+ * nil.
849
+ */
850
+ static VALUE rxml_node_xlink_type(VALUE self)
851
+ {
852
+ xmlNodePtr xnode;
853
+ xlinkType xlt;
854
+
855
+ xnode = rxml_get_xnode(self);
856
+ xlt = xlinkIsLink(xnode->doc, xnode);
857
+
858
+ if (xlt == XLINK_TYPE_NONE)
859
+ return (Qnil);
860
+ else
861
+ return (INT2NUM(xlt));
862
+ }
863
+
864
+ /*
865
+ * call-seq:
866
+ * node.xlink_type_name -> "string"
867
+ *
868
+ * Obtain the type name for this xlink, if applicable.
869
+ * If this is not an xlink node (see +xlink?+), will return
870
+ * nil.
871
+ */
872
+ static VALUE rxml_node_xlink_type_name(VALUE self)
873
+ {
874
+ xmlNodePtr xnode;
875
+ xlinkType xlt;
876
+
877
+ xnode = rxml_get_xnode(self);
878
+ xlt = xlinkIsLink(xnode->doc, xnode);
879
+
880
+ switch (xlt)
881
+ {
882
+ case XLINK_TYPE_NONE:
883
+ return (Qnil);
884
+ case XLINK_TYPE_SIMPLE:
885
+ return (rxml_new_cstr((const xmlChar*)"simple", NULL));
886
+ case XLINK_TYPE_EXTENDED:
887
+ return (rxml_new_cstr((const xmlChar*)"extended", NULL));
888
+ case XLINK_TYPE_EXTENDED_SET:
889
+ return (rxml_new_cstr((const xmlChar*)"extended_set", NULL));
890
+ default:
891
+ rb_fatal("Unknowng xlink type, %d", xlt);
892
+ }
893
+ }
894
+
895
+ /*
896
+ * call-seq:
897
+ * node.name -> "string"
898
+ *
899
+ * Obtain this node's name.
900
+ */
901
+ static VALUE rxml_node_name_get(VALUE self)
902
+ {
903
+ xmlNodePtr xnode;
904
+ const xmlChar *name;
905
+
906
+ xnode = rxml_get_xnode(self);
907
+
908
+ switch (xnode->type)
909
+ {
910
+ case XML_DOCUMENT_NODE:
911
+ #ifdef LIBXML_DOCB_ENABLED
912
+ case XML_DOCB_DOCUMENT_NODE:
913
+ #endif
914
+ case XML_HTML_DOCUMENT_NODE:
915
+ {
916
+ xmlDocPtr doc = (xmlDocPtr) xnode;
917
+ name = doc->URL;
918
+ break;
919
+ }
920
+ case XML_ATTRIBUTE_NODE:
921
+ {
922
+ xmlAttrPtr attr = (xmlAttrPtr) xnode;
923
+ name = attr->name;
924
+ break;
925
+ }
926
+ case XML_NAMESPACE_DECL:
927
+ {
928
+ xmlNsPtr ns = (xmlNsPtr) xnode;
929
+ name = ns->prefix;
930
+ break;
931
+ }
932
+ default:
933
+ name = xnode->name;
934
+ break;
935
+ }
936
+
937
+ if (xnode->name == NULL)
938
+ return (Qnil);
939
+ else
940
+ return (rxml_new_cstr( name, NULL));
941
+ }
942
+
943
+ /*
944
+ * call-seq:
945
+ * node.name = "string"
946
+ *
947
+ * Set this node's name.
948
+ */
949
+ static VALUE rxml_node_name_set(VALUE self, VALUE name)
950
+ {
951
+ xmlNodePtr xnode;
952
+ const xmlChar *xname;
953
+
954
+ Check_Type(name, T_STRING);
955
+ xnode = rxml_get_xnode(self);
956
+ xname = (const xmlChar*)StringValuePtr(name);
957
+
958
+ /* Note: calling xmlNodeSetName() for a text node is ignored by libXML. */
959
+ xmlNodeSetName(xnode, xname);
960
+
961
+ return (Qtrue);
962
+ }
963
+
964
+ /*
965
+ * call-seq:
966
+ * node.next -> XML::Node
967
+ *
968
+ * Returns the next sibling node if one exists.
969
+ */
970
+ static VALUE rxml_node_next_get(VALUE self)
971
+ {
972
+ xmlNodePtr xnode;
973
+
974
+ xnode = rxml_get_xnode(self);
975
+
976
+ if (xnode->next)
977
+ return (rxml_node_wrap(xnode->next));
978
+ else
979
+ return (Qnil);
980
+ }
981
+
982
+ /*
983
+ * call-seq:
984
+ * curr_node.next = node
985
+ *
986
+ * Adds the specified node as the next sibling of the current node.
987
+ * If the node already exists in the document, it is first removed
988
+ * from its existing context. Any adjacent text nodes will be
989
+ * merged together, meaning the returned node may be different
990
+ * than the original node.
991
+ */
992
+ static VALUE rxml_node_next_set(VALUE self, VALUE next)
993
+ {
994
+ return rxml_node_modify_dom(self, next, xmlAddNextSibling);
995
+ }
996
+
997
+ /*
998
+ * call-seq:
999
+ * node.parent -> XML::Node
1000
+ *
1001
+ * Obtain this node's parent node, if any.
1002
+ */
1003
+ static VALUE rxml_node_parent_get(VALUE self)
1004
+ {
1005
+ xmlNodePtr xnode;
1006
+
1007
+ xnode = rxml_get_xnode(self);
1008
+
1009
+ if (xnode->parent)
1010
+ return (rxml_node_wrap(xnode->parent));
1011
+ else
1012
+ return (Qnil);
1013
+ }
1014
+
1015
+ /*
1016
+ * call-seq:
1017
+ * node.path -> path
1018
+ *
1019
+ * Obtain this node's path.
1020
+ */
1021
+ static VALUE rxml_node_path(VALUE self)
1022
+ {
1023
+ xmlNodePtr xnode;
1024
+ xmlChar *path;
1025
+ VALUE result = Qnil;
1026
+
1027
+ xnode = rxml_get_xnode(self);
1028
+ path = xmlGetNodePath(xnode);
1029
+
1030
+ if (path)
1031
+ {
1032
+ result = rxml_new_cstr( path, NULL);
1033
+ xmlFree(path);
1034
+ }
1035
+
1036
+ return result;
1037
+ }
1038
+
1039
+ /*
1040
+ * call-seq:
1041
+ * node.prev -> XML::Node
1042
+ *
1043
+ * Obtain the previous sibling, if any.
1044
+ */
1045
+ static VALUE rxml_node_prev_get(VALUE self)
1046
+ {
1047
+ xmlNodePtr xnode;
1048
+ xmlNodePtr node;
1049
+ xnode = rxml_get_xnode(self);
1050
+
1051
+ switch (xnode->type)
1052
+ {
1053
+ case XML_DOCUMENT_NODE:
1054
+ #ifdef LIBXML_DOCB_ENABLED
1055
+ case XML_DOCB_DOCUMENT_NODE:
1056
+ #endif
1057
+ case XML_HTML_DOCUMENT_NODE:
1058
+ case XML_NAMESPACE_DECL:
1059
+ node = NULL;
1060
+ break;
1061
+ case XML_ATTRIBUTE_NODE:
1062
+ {
1063
+ xmlAttrPtr attr = (xmlAttrPtr) xnode;
1064
+ node = (xmlNodePtr) attr->prev;
1065
+ }
1066
+ break;
1067
+ default:
1068
+ node = xnode->prev;
1069
+ break;
1070
+ }
1071
+
1072
+ if (node == NULL)
1073
+ return (Qnil);
1074
+ else
1075
+ return (rxml_node_wrap(node));
1076
+ }
1077
+
1078
+ /*
1079
+ * call-seq:
1080
+ * curr_node.prev = node
1081
+ *
1082
+ * Adds the specified node as the previous sibling of the current node.
1083
+ * If the node already exists in the document, it is first removed
1084
+ * from its existing context. Any adjacent text nodes will be
1085
+ * merged together, meaning the returned node may be different
1086
+ * than the original node.
1087
+ */
1088
+ static VALUE rxml_node_prev_set(VALUE self, VALUE prev)
1089
+ {
1090
+ return rxml_node_modify_dom(self, prev, xmlAddPrevSibling);
1091
+ }
1092
+
1093
+ /*
1094
+ * call-seq:
1095
+ * node.attributes -> attributes
1096
+ *
1097
+ * Returns the XML::Attributes for this node.
1098
+ */
1099
+ static VALUE rxml_node_attributes_get(VALUE self)
1100
+ {
1101
+ xmlNodePtr xnode;
1102
+
1103
+ xnode = rxml_get_xnode(self);
1104
+ return rxml_attributes_new(xnode);
1105
+ }
1106
+
1107
+ /*
1108
+ * call-seq:
1109
+ * node.property("name") -> "string"
1110
+ * node["name"] -> "string"
1111
+ *
1112
+ * Obtain the named property.
1113
+ */
1114
+ static VALUE rxml_node_attribute_get(VALUE self, VALUE name)
1115
+ {
1116
+ VALUE attributes = rxml_node_attributes_get(self);
1117
+ return rxml_attributes_attribute_get(attributes, name);
1118
+ }
1119
+
1120
+ /*
1121
+ * call-seq:
1122
+ * node["name"] = "string"
1123
+ *
1124
+ * Set the named property.
1125
+ */
1126
+ static VALUE rxml_node_property_set(VALUE self, VALUE name, VALUE value)
1127
+ {
1128
+ VALUE attributes = rxml_node_attributes_get(self);
1129
+ return rxml_attributes_attribute_set(attributes, name, value);
1130
+ }
1131
+
1132
+ /*
1133
+ * call-seq:
1134
+ * node.remove! -> node
1135
+ *
1136
+ * Removes this node and its children from the document tree by setting its document,
1137
+ * parent and siblings to nil. You can add the returned node back into a document.
1138
+ * Otherwise, the node will be freed once any references to it go out of scope.
1139
+ */
1140
+
1141
+ static VALUE rxml_node_remove_ex(VALUE self)
1142
+ {
1143
+ xmlNodePtr xnode = rxml_get_xnode(self);
1144
+
1145
+ // Now unlink the node from its parent
1146
+ xmlUnlinkNode(xnode);
1147
+
1148
+ // Ruby now manages this node
1149
+ rxml_node_manage(xnode, self);
1150
+
1151
+ // Now return the removed node so the user can do something with it
1152
+ return self;
1153
+ }
1154
+
1155
+ /*
1156
+ * call-seq:
1157
+ * curr_node.sibling = node
1158
+ *
1159
+ * Adds the specified node as the end of the current node's list
1160
+ * of siblings. If the node already exists in the document, it
1161
+ * is first removed from its existing context. Any adjacent text
1162
+ * nodes will be merged together, meaning the returned node may
1163
+ * be different than the original node.
1164
+ */
1165
+ static VALUE rxml_node_sibling_set(VALUE self, VALUE sibling)
1166
+ {
1167
+ return rxml_node_modify_dom(self, sibling, xmlAddSibling);
1168
+ }
1169
+
1170
+ /*
1171
+ * call-seq:
1172
+ * text_node.output_escaping? -> (true|false)
1173
+ * element_node.output_escaping? -> (true|false|nil)
1174
+ * attribute_node.output_escaping? -> (true|false|nil)
1175
+ * other_node.output_escaping? -> (nil)
1176
+ *
1177
+ * Determine whether this node escapes it's output or not.
1178
+ *
1179
+ * Text nodes return only +true+ or +false+. Element and attribute nodes
1180
+ * examine their immediate text node children to determine the value.
1181
+ * Any other type of node always returns +nil+.
1182
+ *
1183
+ * If an element or attribute node has at least one immediate child text node
1184
+ * and all the immediate text node children have the same +output_escaping?+
1185
+ * value, that value is returned. Otherwise, +nil+ is returned.
1186
+ */
1187
+ static VALUE rxml_node_output_escaping_q(VALUE self)
1188
+ {
1189
+ xmlNodePtr xnode;
1190
+ xnode = rxml_get_xnode(self);
1191
+
1192
+ switch (xnode->type) {
1193
+ case XML_TEXT_NODE:
1194
+ return xnode->name==xmlStringTextNoenc ? Qfalse : Qtrue;
1195
+ case XML_ELEMENT_NODE:
1196
+ case XML_ATTRIBUTE_NODE:
1197
+ {
1198
+ xmlNodePtr tmp = xnode->children;
1199
+ const xmlChar *match = NULL;
1200
+
1201
+ /* Find the first text node and use it as the reference. */
1202
+ while (tmp && tmp->type != XML_TEXT_NODE)
1203
+ tmp = tmp->next;
1204
+ if (! tmp)
1205
+ return Qnil;
1206
+ match = tmp->name;
1207
+
1208
+ /* Walk the remaining text nodes until we run out or one doesn't match. */
1209
+ while (tmp && (tmp->type != XML_TEXT_NODE || match == tmp->name))
1210
+ tmp = tmp->next;
1211
+
1212
+ /* We're left with either the mismatched node or the aggregate result. */
1213
+ return tmp ? Qnil : (match==xmlStringTextNoenc ? Qfalse : Qtrue);
1214
+ }
1215
+ break;
1216
+ default:
1217
+ return Qnil;
1218
+ }
1219
+ }
1220
+
1221
+ /*
1222
+ * call-seq:
1223
+ * text_node.output_escaping = true|false
1224
+ * element_node.output_escaping = true|false
1225
+ * attribute_node.output_escaping = true|false
1226
+ *
1227
+ * Controls whether this text node or the immediate text node children of an
1228
+ * element or attribute node escapes their output. Any other type of node
1229
+ * will simply ignore this operation.
1230
+ *
1231
+ * Text nodes which are added to an element or attribute node will be affected
1232
+ * by any previous setting of this property.
1233
+ */
1234
+ static VALUE rxml_node_output_escaping_set(VALUE self, VALUE value)
1235
+ {
1236
+ xmlNodePtr xnode;
1237
+ xnode = rxml_get_xnode(self);
1238
+
1239
+ switch (xnode->type) {
1240
+ case XML_TEXT_NODE:
1241
+ xnode->name = (value != Qfalse && value != Qnil) ? xmlStringText : xmlStringTextNoenc;
1242
+ break;
1243
+ case XML_ELEMENT_NODE:
1244
+ case XML_ATTRIBUTE_NODE:
1245
+ {
1246
+ const xmlChar *name = (value != Qfalse && value != Qnil) ? xmlStringText : xmlStringTextNoenc;
1247
+ xmlNodePtr tmp;
1248
+ for (tmp = xnode->children; tmp; tmp = tmp->next)
1249
+ if (tmp->type == XML_TEXT_NODE)
1250
+ tmp->name = name;
1251
+ }
1252
+ break;
1253
+ default:
1254
+ return Qnil;
1255
+ }
1256
+
1257
+ return (value!=Qfalse && value!=Qnil) ? Qtrue : Qfalse;
1258
+ }
1259
+
1260
+ /*
1261
+ * call-seq:
1262
+ * node.space_preserve -> (true|false)
1263
+ *
1264
+ * Determine whether this node preserves whitespace.
1265
+ */
1266
+ static VALUE rxml_node_space_preserve_get(VALUE self)
1267
+ {
1268
+ xmlNodePtr xnode;
1269
+
1270
+ xnode = rxml_get_xnode(self);
1271
+ return (INT2NUM(xmlNodeGetSpacePreserve(xnode)));
1272
+ }
1273
+
1274
+ /*
1275
+ * call-seq:
1276
+ * node.space_preserve = true|false
1277
+ *
1278
+ * Control whether this node preserves whitespace.
1279
+ */
1280
+ static VALUE rxml_node_space_preserve_set(VALUE self, VALUE value)
1281
+ {
1282
+ xmlNodePtr xnode;
1283
+ xnode = rxml_get_xnode(self);
1284
+
1285
+ if (value == Qfalse)
1286
+ xmlNodeSetSpacePreserve(xnode, 0);
1287
+ else
1288
+ xmlNodeSetSpacePreserve(xnode, 1);
1289
+
1290
+ return (Qnil);
1291
+ }
1292
+
1293
+ /*
1294
+ * call-seq:
1295
+ * node.type -> num
1296
+ *
1297
+ * Obtain this node's type identifier.
1298
+ */
1299
+ static VALUE rxml_node_type(VALUE self)
1300
+ {
1301
+ xmlNodePtr xnode;
1302
+ xnode = rxml_get_xnode(self);
1303
+ return (INT2NUM(xnode->type));
1304
+ }
1305
+
1306
+ /*
1307
+ * call-seq:
1308
+ * node.copy -> XML::Node
1309
+ *
1310
+ * Creates a copy of this node. To create a
1311
+ * shallow copy set the deep parameter to false.
1312
+ * To create a deep copy set the deep parameter
1313
+ * to true.
1314
+ *
1315
+ */
1316
+ static VALUE rxml_node_copy(VALUE self, VALUE deep)
1317
+ {
1318
+ xmlNodePtr xnode;
1319
+ xmlNodePtr xcopy;
1320
+ int recursive = (deep == Qnil || deep == Qfalse) ? 0 : 1;
1321
+ xnode = rxml_get_xnode(self);
1322
+
1323
+ xcopy = xmlCopyNode(xnode, recursive);
1324
+
1325
+ if (xcopy)
1326
+ return rxml_node_wrap(xcopy);
1327
+ else
1328
+ return Qnil;
1329
+ }
1330
+
1331
+ void rxml_init_node(void)
1332
+ {
1333
+ cXMLNode = rb_define_class_under(mXML, "Node", rb_cObject);
1334
+
1335
+ rb_define_const(cXMLNode, "SPACE_DEFAULT", INT2NUM(0));
1336
+ rb_define_const(cXMLNode, "SPACE_PRESERVE", INT2NUM(1));
1337
+ rb_define_const(cXMLNode, "SPACE_NOT_INHERIT", INT2NUM(-1));
1338
+ rb_define_const(cXMLNode, "XLINK_ACTUATE_AUTO", INT2NUM(1));
1339
+ rb_define_const(cXMLNode, "XLINK_ACTUATE_NONE", INT2NUM(0));
1340
+ rb_define_const(cXMLNode, "XLINK_ACTUATE_ONREQUEST", INT2NUM(2));
1341
+ rb_define_const(cXMLNode, "XLINK_SHOW_EMBED", INT2NUM(2));
1342
+ rb_define_const(cXMLNode, "XLINK_SHOW_NEW", INT2NUM(1));
1343
+ rb_define_const(cXMLNode, "XLINK_SHOW_NONE", INT2NUM(0));
1344
+ rb_define_const(cXMLNode, "XLINK_SHOW_REPLACE", INT2NUM(3));
1345
+ rb_define_const(cXMLNode, "XLINK_TYPE_EXTENDED", INT2NUM(2));
1346
+ rb_define_const(cXMLNode, "XLINK_TYPE_EXTENDED_SET", INT2NUM(3));
1347
+ rb_define_const(cXMLNode, "XLINK_TYPE_NONE", INT2NUM(0));
1348
+ rb_define_const(cXMLNode, "XLINK_TYPE_SIMPLE", INT2NUM(1));
1349
+
1350
+ rb_define_const(cXMLNode, "ELEMENT_NODE", INT2FIX(XML_ELEMENT_NODE));
1351
+ rb_define_const(cXMLNode, "ATTRIBUTE_NODE", INT2FIX(XML_ATTRIBUTE_NODE));
1352
+ rb_define_const(cXMLNode, "TEXT_NODE", INT2FIX(XML_TEXT_NODE));
1353
+ rb_define_const(cXMLNode, "CDATA_SECTION_NODE", INT2FIX(XML_CDATA_SECTION_NODE));
1354
+ rb_define_const(cXMLNode, "ENTITY_REF_NODE", INT2FIX(XML_ENTITY_REF_NODE));
1355
+ rb_define_const(cXMLNode, "ENTITY_NODE", INT2FIX(XML_ENTITY_NODE));
1356
+ rb_define_const(cXMLNode, "PI_NODE", INT2FIX(XML_PI_NODE));
1357
+ rb_define_const(cXMLNode, "COMMENT_NODE", INT2FIX(XML_COMMENT_NODE));
1358
+ rb_define_const(cXMLNode, "DOCUMENT_NODE", INT2FIX(XML_DOCUMENT_NODE));
1359
+ rb_define_const(cXMLNode, "DOCUMENT_TYPE_NODE", INT2FIX(XML_DOCUMENT_TYPE_NODE));
1360
+ rb_define_const(cXMLNode, "DOCUMENT_FRAG_NODE", INT2FIX(XML_DOCUMENT_FRAG_NODE));
1361
+ rb_define_const(cXMLNode, "NOTATION_NODE", INT2FIX(XML_NOTATION_NODE));
1362
+ rb_define_const(cXMLNode, "HTML_DOCUMENT_NODE", INT2FIX(XML_HTML_DOCUMENT_NODE));
1363
+ rb_define_const(cXMLNode, "DTD_NODE", INT2FIX(XML_DTD_NODE));
1364
+ rb_define_const(cXMLNode, "ELEMENT_DECL", INT2FIX(XML_ELEMENT_DECL));
1365
+ rb_define_const(cXMLNode, "ATTRIBUTE_DECL", INT2FIX(XML_ATTRIBUTE_DECL));
1366
+ rb_define_const(cXMLNode, "ENTITY_DECL", INT2FIX(XML_ENTITY_DECL));
1367
+ rb_define_const(cXMLNode, "NAMESPACE_DECL", INT2FIX(XML_NAMESPACE_DECL));
1368
+ rb_define_const(cXMLNode, "XINCLUDE_START", INT2FIX(XML_XINCLUDE_START));
1369
+ rb_define_const(cXMLNode, "XINCLUDE_END", INT2FIX(XML_XINCLUDE_END));
1370
+
1371
+ #ifdef LIBXML_DOCB_ENABLED
1372
+ rb_define_const(cXMLNode, "DOCB_DOCUMENT_NODE", INT2FIX(XML_DOCB_DOCUMENT_NODE));
1373
+ #else
1374
+ rb_define_const(cXMLNode, "DOCB_DOCUMENT_NODE", Qnil);
1375
+ #endif
1376
+
1377
+ rb_define_singleton_method(cXMLNode, "new_cdata", rxml_node_new_cdata, -1);
1378
+ rb_define_singleton_method(cXMLNode, "new_comment", rxml_node_new_comment, -1);
1379
+ rb_define_singleton_method(cXMLNode, "new_pi", rxml_node_new_pi, -1);
1380
+ rb_define_singleton_method(cXMLNode, "new_text", rxml_node_new_text, 1);
1381
+
1382
+ /* Initialization */
1383
+ rb_define_alloc_func(cXMLNode, rxml_node_alloc);
1384
+ rb_define_method(cXMLNode, "initialize", rxml_node_initialize, -1);
1385
+
1386
+ /* Traversal */
1387
+ rb_include_module(cXMLNode, rb_mEnumerable);
1388
+ rb_define_method(cXMLNode, "[]", rxml_node_attribute_get, 1);
1389
+ rb_define_method(cXMLNode, "each", rxml_node_each, 0);
1390
+ rb_define_method(cXMLNode, "first", rxml_node_first_get, 0);
1391
+ rb_define_method(cXMLNode, "last", rxml_node_last_get, 0);
1392
+ rb_define_method(cXMLNode, "next", rxml_node_next_get, 0);
1393
+ rb_define_method(cXMLNode, "parent", rxml_node_parent_get, 0);
1394
+ rb_define_method(cXMLNode, "prev", rxml_node_prev_get, 0);
1395
+
1396
+ /* Modification */
1397
+ rb_define_method(cXMLNode, "[]=", rxml_node_property_set, 2);
1398
+ rb_define_method(cXMLNode, "<<", rxml_node_content_add, 1);
1399
+ rb_define_method(cXMLNode, "sibling=", rxml_node_sibling_set, 1);
1400
+ rb_define_method(cXMLNode, "next=", rxml_node_next_set, 1);
1401
+ rb_define_method(cXMLNode, "prev=", rxml_node_prev_set, 1);
1402
+
1403
+ /* Rest of the node api */
1404
+ rb_define_method(cXMLNode, "attributes", rxml_node_attributes_get, 0);
1405
+ rb_define_method(cXMLNode, "base_uri", rxml_node_base_uri_get, 0);
1406
+ rb_define_method(cXMLNode, "base_uri=", rxml_node_base_uri_set, 1);
1407
+ rb_define_method(cXMLNode, "blank?", rxml_node_empty_q, 0);
1408
+ rb_define_method(cXMLNode, "copy", rxml_node_copy, 1);
1409
+ rb_define_method(cXMLNode, "content", rxml_node_content_get, 0);
1410
+ rb_define_method(cXMLNode, "content=", rxml_node_content_set, 1);
1411
+ rb_define_method(cXMLNode, "debug", rxml_node_debug, 0);
1412
+ rb_define_method(cXMLNode, "doc", rxml_node_doc, 0);
1413
+ rb_define_method(cXMLNode, "empty?", rxml_node_empty_q, 0);
1414
+ rb_define_method(cXMLNode, "eql?", rxml_node_eql_q, 1);
1415
+ rb_define_method(cXMLNode, "lang", rxml_node_lang_get, 0);
1416
+ rb_define_method(cXMLNode, "lang=", rxml_node_lang_set, 1);
1417
+ rb_define_method(cXMLNode, "line_num", rxml_node_line_num, 0);
1418
+ rb_define_method(cXMLNode, "name", rxml_node_name_get, 0);
1419
+ rb_define_method(cXMLNode, "name=", rxml_node_name_set, 1);
1420
+ rb_define_method(cXMLNode, "node_type", rxml_node_type, 0);
1421
+ rb_define_method(cXMLNode, "output_escaping?", rxml_node_output_escaping_q, 0);
1422
+ rb_define_method(cXMLNode, "output_escaping=", rxml_node_output_escaping_set, 1);
1423
+ rb_define_method(cXMLNode, "path", rxml_node_path, 0);
1424
+ rb_define_method(cXMLNode, "remove!", rxml_node_remove_ex, 0);
1425
+ rb_define_method(cXMLNode, "space_preserve", rxml_node_space_preserve_get, 0);
1426
+ rb_define_method(cXMLNode, "space_preserve=", rxml_node_space_preserve_set, 1);
1427
+ rb_define_method(cXMLNode, "to_s", rxml_node_to_s, -1);
1428
+ rb_define_method(cXMLNode, "xlink?", rxml_node_xlink_q, 0);
1429
+ rb_define_method(cXMLNode, "xlink_type", rxml_node_xlink_type, 0);
1430
+ rb_define_method(cXMLNode, "xlink_type_name", rxml_node_xlink_type_name, 0);
1431
+
1432
+ rb_define_alias(cXMLNode, "==", "eql?");
1433
+ }