libxml-ruby 1.1.2-x86-mswin32-60 → 1.1.3-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
@@ -51,7 +51,7 @@ void rxml_dtd_mark(xmlDtdPtr xdtd)
51
51
  return;
52
52
  }
53
53
 
54
- rxml_node_mark_common((xmlNodePtr) xdtd);
54
+ rxml_node_mark((xmlNodePtr) xdtd);
55
55
  }
56
56
 
57
57
 
@@ -81,6 +81,7 @@ static VALUE rxml_error_reset_handler(VALUE self)
81
81
  VALUE rxml_error_wrap(xmlErrorPtr xerror)
82
82
  {
83
83
  VALUE result = Qnil;
84
+
84
85
  if (xerror->message)
85
86
  result = rb_exc_new2(eXMLError, xerror->message);
86
87
  else
@@ -171,6 +172,10 @@ void rxml_raise(xmlErrorPtr xerror)
171
172
 
172
173
  void rxml_init_error()
173
174
  {
175
+ ON_ERROR_METHOD = rb_intern("on_error");
176
+ CALL_METHOD = rb_intern("call");
177
+ ERROR_HANDLER_ID = rb_intern("@@__error_handler_callback__");
178
+
174
179
  /* Intercept libxml error handlers */
175
180
  xmlSetStructuredErrorFunc(NULL, structuredErrorFunc);
176
181
 
@@ -179,11 +184,7 @@ void rxml_init_error()
179
184
  rb_define_singleton_method(eXMLError, "set_handler", rxml_error_set_handler, 0);
180
185
  rb_define_singleton_method(eXMLError, "reset_handler", rxml_error_reset_handler, 0);
181
186
 
182
- ON_ERROR_METHOD = rb_intern("on_error");
183
- CALL_METHOD = rb_intern("call");
184
-
185
187
  /* Ruby callback to receive errors - set it to nil by default. */
186
- ERROR_HANDLER_ID = rb_intern("@@__error_handler_callback__");
187
188
  rxml_set_handler(eXMLError, Qnil);
188
189
 
189
190
  /* Error attributes */
@@ -155,6 +155,12 @@ static VALUE rxml_html_parser_context_file(VALUE klass, VALUE file)
155
155
  if (!ctxt)
156
156
  rxml_raise(&xmlLastError);
157
157
 
158
+ /* This is annoying, but xmlInitParserCtxt (called indirectly above) and
159
+ xmlCtxtUseOptionsInternal (called below) initialize slightly different
160
+ context options, in particular XML_PARSE_NODICT which xmlInitParserCtxt
161
+ sets to 0 and xmlCtxtUseOptionsInternal sets to 1. So we have to call both. */
162
+ htmlCtxtUseOptions(ctxt, rxml_libxml_default_options());
163
+
158
164
  return rxml_html_parser_context_wrap(ctxt);
159
165
  }
160
166
 
@@ -187,6 +193,12 @@ static VALUE rxml_html_parser_context_io(VALUE klass, VALUE io)
187
193
  rxml_raise(&xmlLastError);
188
194
  }
189
195
 
196
+ /* This is annoying, but xmlInitParserCtxt (called indirectly above) and
197
+ xmlCtxtUseOptionsInternal (called below) initialize slightly different
198
+ context options, in particular XML_PARSE_NODICT which xmlInitParserCtxt
199
+ sets to 0 and xmlCtxtUseOptionsInternal sets to 1. So we have to call both. */
200
+ htmlCtxtUseOptions(ctxt, rxml_libxml_default_options());
201
+
190
202
  stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE);
191
203
 
192
204
  if (!stream)
@@ -226,6 +238,12 @@ static VALUE rxml_html_parser_context_string(VALUE klass, VALUE string)
226
238
  if (!ctxt)
227
239
  rxml_raise(&xmlLastError);
228
240
 
241
+ /* This is annoying, but xmlInitParserCtxt (called indirectly above) and
242
+ xmlCtxtUseOptionsInternal (called below) initialize slightly different
243
+ context options, in particular XML_PARSE_NODICT which xmlInitParserCtxt
244
+ sets to 0 and xmlCtxtUseOptionsInternal sets to 1. So we have to call both. */
245
+ htmlCtxtUseOptions(ctxt, rxml_libxml_default_options());
246
+
229
247
  htmlDefaultSAXHandlerInit();
230
248
  if (ctxt->sax != NULL)
231
249
  memcpy(ctxt->sax, &htmlDefaultSAXHandler, sizeof(xmlSAXHandlerV1));
@@ -1,5 +1,6 @@
1
1
  #include "ruby_libxml.h"
2
2
  #include "ruby_xml_node.h"
3
+ #include <assert.h>
3
4
 
4
5
  VALUE cXMLNode;
5
6
 
@@ -13,18 +14,6 @@ VALUE cXMLNode;
13
14
  * documented in the DOM Level 3 specification found at:
14
15
  * http://www.w3.org/TR/DOM-Level-3-Core/. */
15
16
 
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
17
 
29
18
  /* Memory management:
30
19
  *
@@ -40,13 +29,18 @@ VALUE check_string_or_symbol(VALUE val)
40
29
  * * Using the mark function to keep alive any top level, free
41
30
  * standing nodes Ruby is referencing via the node or its children.
42
31
  *
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.
32
+ * In general use, this will cause Ruby nodes to be freed before
33
+ * a libxml document. When a Ruby node is freed, the _private
34
+ * field is set back to null.
35
+ *
36
+ * In the sweep phase in Ruby 1.9.1, the document is freed before
37
+ * the nodes. To support that, the bindingds register a callback
38
+ * function with libxml that is called each time a node is freed.
39
+ * In that case, the data_ptr is set to null, so the bindings
40
+ * can recognize the situtation.
47
41
  */
48
42
 
49
- void rxml_node_deregisterNode(xmlNodePtr xnode)
43
+ static void rxml_node_deregisterNode(xmlNodePtr xnode)
50
44
  {
51
45
  /* Has the node been wrapped and exposed to Ruby? */
52
46
  if (xnode->_private)
@@ -55,59 +49,40 @@ void rxml_node_deregisterNode(xmlNodePtr xnode)
55
49
  then dislabe the dfree function so that Ruby will not
56
50
  try to free the node a second time. */
57
51
  VALUE node = (VALUE) xnode->_private;
58
- RDATA(node)->dmark = NULL;
59
- RDATA(node)->dfree = NULL;
60
- xnode->_private = NULL;
52
+ RDATA(node)->data = NULL;
61
53
  }
62
54
  }
63
55
 
64
- void rxml_node_free(xmlNodePtr xnode)
56
+ static void rxml_node_free(xmlNodePtr xnode)
65
57
  {
66
- /* The Ruby object that wraps the node no longer exists. */
58
+ /* Either the node has been created yet in initialize
59
+ or it has been freed by libxml already in Ruby's
60
+ mark phase. */
61
+ if (xnode == NULL)
62
+ return;
63
+
64
+ /* The ruby object wrapping the xml object no longer exists. */
67
65
  xnode->_private = NULL;
68
66
 
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)
67
+ /* Ruby is responsible for freeing this node since it
68
+ has no parent. */
69
+ if (xnode->parent == NULL)
73
70
  xmlFreeNode(xnode);
74
71
  }
75
72
 
76
- void rxml_node_mark_common(xmlNodePtr xnode)
73
+ void rxml_node_mark(xmlNodePtr xnode)
77
74
  {
78
- if (xnode->parent == NULL)
75
+ /* Either the node has been created yet in initialize
76
+ or it has been freed by libxml already in Ruby's
77
+ mark phase. */
78
+ if (xnode == NULL)
79
79
  return;
80
80
 
81
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
82
  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);
83
+
84
+ if (xnode->parent != NULL)
85
+ rb_gc_mark((VALUE) xnode->_private);
111
86
  }
112
87
 
113
88
  VALUE rxml_node_wrap(xmlNodePtr xnode)
@@ -119,9 +94,6 @@ VALUE rxml_node_wrap(xmlNodePtr xnode)
119
94
  }
120
95
  else
121
96
  {
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
97
  VALUE node = Data_Wrap_Struct(cXMLNode, rxml_node_mark, rxml_node_free, xnode);
126
98
  xnode->_private = (void*) node;
127
99
  return node;
@@ -130,7 +102,8 @@ VALUE rxml_node_wrap(xmlNodePtr xnode)
130
102
 
131
103
  static VALUE rxml_node_alloc(VALUE klass)
132
104
  {
133
- /* Ruby is responsible for freeing this node not libxml. */
105
+ /* Ruby is responsible for freeing this node not libxml but don't set
106
+ up mark and free yet until we assign the node. */
134
107
  return Data_Wrap_Struct(klass, rxml_node_mark, rxml_node_free, NULL);
135
108
  }
136
109
 
@@ -217,6 +190,8 @@ static VALUE rxml_node_new_text(VALUE klass, VALUE content)
217
190
  return rxml_node_wrap(xnode);
218
191
  }
219
192
 
193
+ static VALUE rxml_node_content_set(VALUE self, VALUE content);
194
+
220
195
  /*
221
196
  * call-seq:
222
197
  * XML::Node.initialize(name, content = nil, namespace = nil) -> XML::Node
@@ -234,14 +209,19 @@ static VALUE rxml_node_initialize(int argc, VALUE *argv, VALUE self)
234
209
 
235
210
  rb_scan_args(argc, argv, "12", &name, &content, &ns);
236
211
 
237
- name = check_string_or_symbol(name);
212
+ name = rb_obj_as_string(name);
238
213
 
239
214
  if (!NIL_P(ns))
240
215
  Data_Get_Struct(ns, xmlNs, xns);
241
216
 
242
217
  xnode = xmlNewNode(xns, (xmlChar*) StringValuePtr(name));
218
+
219
+ if (xnode == NULL)
220
+ rxml_raise(&xmlLastError);
221
+
222
+ /* Link the Ruby object to the libxml object and vice-versa. */
243
223
  xnode->_private = (void*) self;
244
- DATA_PTR( self) = xnode;
224
+ DATA_PTR(self) = xnode;
245
225
 
246
226
  if (!NIL_P(content))
247
227
  rxml_node_content_set(self, content);
@@ -249,6 +229,36 @@ static VALUE rxml_node_initialize(int argc, VALUE *argv, VALUE self)
249
229
  return self;
250
230
  }
251
231
 
232
+ static VALUE rxml_node_modify_dom(VALUE self, VALUE target,
233
+ xmlNodePtr (*xmlFunc)(xmlNodePtr, xmlNodePtr))
234
+ {
235
+ xmlNodePtr xnode, xtarget, xresult;
236
+
237
+ if (rb_obj_is_kind_of(target, cXMLNode) == Qfalse)
238
+ rb_raise(rb_eTypeError, "Must pass an XML::Node object");
239
+
240
+ Data_Get_Struct(self, xmlNode, xnode);
241
+ Data_Get_Struct(target, xmlNode, xtarget);
242
+
243
+ if (xtarget->doc != NULL && xtarget->doc != xnode->doc)
244
+ rb_raise(eXMLError, "Nodes belong to different documents. You must first import the by calling XML::Document.import");
245
+
246
+ /* This target node could be freed here. */
247
+ xresult = xmlFunc(xnode, xtarget);
248
+
249
+ if (!xresult)
250
+ rxml_raise(&xmlLastError);
251
+
252
+ /* Was the target freed? If yes, then wrap the new node */
253
+ if (xresult != xtarget)
254
+ {
255
+ RDATA(target)->data = xresult;
256
+ xresult->_private = (void*) target;
257
+ }
258
+
259
+ return target;
260
+ }
261
+
252
262
  /*
253
263
  * call-seq:
254
264
  * node.base_uri -> "uri"
@@ -404,51 +414,20 @@ static VALUE rxml_node_first_get(VALUE self)
404
414
  return (Qnil);
405
415
  }
406
416
 
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
417
 
433
418
  /*
434
419
  * call-seq:
435
- * node.child = node
420
+ * curr_node << "<p>A paragraph</p>"
421
+ * curr_node << node
436
422
  *
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
423
+ * Add the specified text or XML::Node as a new child node to the
424
+ * current node.
447
425
  *
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.
426
+ * If the specified argument is a string, it should be a raw string
427
+ * that contains unescaped XML special characters. Entity references
428
+ * are not supported.
429
+ *
430
+ * The method will return the current node.
452
431
  */
453
432
  static VALUE rxml_node_content_add(VALUE self, VALUE obj)
454
433
  {
@@ -461,8 +440,11 @@ static VALUE rxml_node_content_add(VALUE self, VALUE obj)
461
440
  * danj 070827
462
441
  */
463
442
  if (rb_obj_is_kind_of(obj, cXMLNode))
464
- {
465
- rxml_node_child_set(self, obj);
443
+ {
444
+ xmlNodePtr xtarget;
445
+ Data_Get_Struct(obj, xmlNode, xtarget);
446
+ xmlUnlinkNode(xtarget);
447
+ rxml_node_modify_dom(self, obj, xmlAddChild);
466
448
  }
467
449
  else
468
450
  {
@@ -472,18 +454,7 @@ static VALUE rxml_node_content_add(VALUE self, VALUE obj)
472
454
 
473
455
  xmlNodeAddContent(xnode, (xmlChar*) StringValuePtr(str));
474
456
  }
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);
457
+ return self;
487
458
  }
488
459
 
489
460
  /*
@@ -923,7 +894,7 @@ static VALUE rxml_node_name_set(VALUE self, VALUE name)
923
894
  * call-seq:
924
895
  * node.next -> XML::Node
925
896
  *
926
- * Obtain the next sibling node, if any.
897
+ * Returns the next sibling node if one exists.
927
898
  */
928
899
  static VALUE rxml_node_next_get(VALUE self)
929
900
  {
@@ -939,25 +910,17 @@ static VALUE rxml_node_next_get(VALUE self)
939
910
 
940
911
  /*
941
912
  * call-seq:
942
- * node.next = node
913
+ * curr_node.next = node
943
914
  *
944
- * Insert the specified node as this node's next sibling.
915
+ * Adds the specified node as the next sibling of the current node.
916
+ * If the node already exists in the document, it is first removed
917
+ * from its existing context. Any adjacent text nodes will be
918
+ * merged together, meaning the returned node may be different
919
+ * than the original node.
945
920
  */
946
921
  static VALUE rxml_node_next_set(VALUE self, VALUE next)
947
922
  {
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);
923
+ return rxml_node_modify_dom(self, next, xmlAddNextSibling);
961
924
  }
962
925
 
963
926
  /*
@@ -1050,25 +1013,17 @@ static VALUE rxml_node_prev_get(VALUE self)
1050
1013
 
1051
1014
  /*
1052
1015
  * call-seq:
1053
- * node.prev = node
1016
+ * curr_node.prev = node
1054
1017
  *
1055
- * Insert the specified node as this node's previous sibling.
1018
+ * Adds the specified node as the previous sibling of the current node.
1019
+ * If the node already exists in the document, it is first removed
1020
+ * from its existing context. Any adjacent text nodes will be
1021
+ * merged together, meaning the returned node may be different
1022
+ * than the original node.
1056
1023
  */
1057
1024
  static VALUE rxml_node_prev_set(VALUE self, VALUE prev)
1058
1025
  {
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);
1026
+ return rxml_node_modify_dom(self, prev, xmlAddPrevSibling);
1072
1027
  }
1073
1028
 
1074
1029
  /*
@@ -1114,51 +1069,52 @@ static VALUE rxml_node_property_set(VALUE self, VALUE name, VALUE value)
1114
1069
  * call-seq:
1115
1070
  * node.remove! -> node
1116
1071
  *
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. */
1072
+ * Removes this node and its children from the document tree by setting its document,
1073
+ * parent and siblings to nil. You can add the returned node back into a document.
1074
+ * Otherwise, the node will be freed once any references to it go out of scope.
1075
+ */
1123
1076
 
1124
1077
  static VALUE rxml_node_remove_ex(VALUE self)
1125
1078
  {
1126
- xmlNodePtr xnode;
1079
+ xmlNodePtr xnode, xresult;
1127
1080
  Data_Get_Struct(self, xmlNode, xnode);
1128
1081
 
1129
- /* Unlink the node from its parent. */
1082
+ /* First unlink the node from its parent. */
1130
1083
  xmlUnlinkNode(xnode);
1131
1084
 
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);
1085
+ /* Now copy the node we want to remove and make the
1086
+ current Ruby object point to it. We do this because
1087
+ a node has a number of dependencies on its parent
1088
+ document - its name (if using a dictionary), entities,
1089
+ namespaces, etc. For a node to live on its own, it
1090
+ needs to get its own copies of this information.*/
1091
+ xresult = xmlDocCopyNode(xnode, NULL, 1);
1092
+
1093
+ /* Now free the original node. */
1094
+ xmlFreeNode(xnode);
1095
+
1096
+ /* Now wrap the new node */
1097
+ RDATA(self)->data = xresult;
1098
+ xresult->_private = (void*) self;
1135
1099
 
1136
1100
  /* Now return the removed node so the user can
1137
- do something wiht it.*/
1101
+ do something with it.*/
1138
1102
  return self;
1139
1103
  }
1140
1104
 
1141
1105
  /*
1142
1106
  * call-seq:
1143
- * node.sibling(node) -> XML::Node
1107
+ * curr_node.sibling = node
1144
1108
  *
1145
- * Add the specified node as a sibling of this node.
1109
+ * Adds the specified node as the end of the current node's list
1110
+ * of siblings. If the node already exists in the document, it
1111
+ * is first removed from its existing context. Any adjacent text
1112
+ * nodes will be merged together, meaning the returned node may
1113
+ * be different than the original node.
1146
1114
  */
1147
1115
  static VALUE rxml_node_sibling_set(VALUE self, VALUE sibling)
1148
1116
  {
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);
1117
+ return rxml_node_modify_dom(self, sibling, xmlAddSibling);
1162
1118
  }
1163
1119
 
1164
1120
  /*
@@ -1389,10 +1345,8 @@ void rxml_init_node(void)
1389
1345
  rb_define_method(cXMLNode, "prev", rxml_node_prev_get, 0);
1390
1346
 
1391
1347
  /* Modification */
1392
- rb_define_method(cXMLNode, "<<", rxml_node_content_add, 1);
1393
1348
  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);
1349
+ rb_define_method(cXMLNode, "<<", rxml_node_content_add, 1);
1396
1350
  rb_define_method(cXMLNode, "sibling=", rxml_node_sibling_set, 1);
1397
1351
  rb_define_method(cXMLNode, "next=", rxml_node_next_set, 1);
1398
1352
  rb_define_method(cXMLNode, "prev=", rxml_node_prev_set, 1);