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