libxml-ruby 0.9.9 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,1428 +1,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 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 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
+ }