libxml-ruby 1.1.1 → 1.1.2

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