libxml-ruby 2.7.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +6 -14
  2. data/HISTORY +33 -11
  3. data/README.rdoc +7 -7
  4. data/Rakefile +80 -77
  5. data/ext/libxml/extconf.h +4 -5
  6. data/ext/libxml/extconf.rb +57 -118
  7. data/ext/libxml/libxml.c +4 -0
  8. data/ext/libxml/ruby_xml.c +977 -893
  9. data/ext/libxml/ruby_xml.h +20 -10
  10. data/ext/libxml/ruby_xml_attr.c +333 -333
  11. data/ext/libxml/ruby_xml_attr_decl.c +2 -2
  12. data/ext/libxml/ruby_xml_cbg.c +85 -85
  13. data/ext/libxml/ruby_xml_document.c +1133 -1147
  14. data/ext/libxml/ruby_xml_dtd.c +261 -268
  15. data/ext/libxml/ruby_xml_encoding.c +262 -260
  16. data/ext/libxml/ruby_xml_encoding.h +19 -19
  17. data/ext/libxml/ruby_xml_html_parser_context.c +337 -338
  18. data/ext/libxml/ruby_xml_input_cbg.c +191 -191
  19. data/ext/libxml/ruby_xml_io.c +52 -50
  20. data/ext/libxml/ruby_xml_namespace.c +2 -2
  21. data/ext/libxml/ruby_xml_node.c +1446 -1452
  22. data/ext/libxml/ruby_xml_parser_context.c +999 -1001
  23. data/ext/libxml/ruby_xml_reader.c +1226 -1228
  24. data/ext/libxml/ruby_xml_relaxng.c +110 -111
  25. data/ext/libxml/ruby_xml_sax2_handler.c +326 -328
  26. data/ext/libxml/ruby_xml_schema.c +300 -301
  27. data/ext/libxml/ruby_xml_version.h +3 -3
  28. data/ext/libxml/ruby_xml_writer.c +34 -16
  29. data/ext/libxml/ruby_xml_xpath.c +188 -187
  30. data/ext/libxml/ruby_xml_xpath_context.c +360 -361
  31. data/ext/libxml/ruby_xml_xpath_object.c +335 -335
  32. data/libxml-ruby.gemspec +47 -44
  33. data/test/tc_attr.rb +5 -7
  34. data/test/tc_attr_decl.rb +5 -6
  35. data/test/tc_attributes.rb +1 -2
  36. data/test/tc_canonicalize.rb +1 -2
  37. data/test/tc_deprecated_require.rb +1 -2
  38. data/test/tc_document.rb +4 -5
  39. data/test/tc_document_write.rb +2 -3
  40. data/test/tc_dtd.rb +4 -5
  41. data/test/tc_encoding.rb +126 -126
  42. data/test/tc_encoding_sax.rb +4 -3
  43. data/test/tc_error.rb +14 -15
  44. data/test/tc_html_parser.rb +15 -7
  45. data/test/tc_html_parser_context.rb +1 -2
  46. data/test/tc_namespace.rb +2 -3
  47. data/test/tc_namespaces.rb +5 -6
  48. data/test/tc_node.rb +2 -3
  49. data/test/tc_node_cdata.rb +2 -3
  50. data/test/tc_node_comment.rb +1 -2
  51. data/test/tc_node_copy.rb +1 -2
  52. data/test/tc_node_edit.rb +5 -7
  53. data/test/tc_node_pi.rb +1 -2
  54. data/test/tc_node_text.rb +2 -3
  55. data/test/tc_node_write.rb +2 -3
  56. data/test/tc_node_xlink.rb +1 -2
  57. data/test/tc_parser.rb +18 -24
  58. data/test/tc_parser_context.rb +6 -7
  59. data/test/tc_properties.rb +1 -2
  60. data/test/tc_reader.rb +9 -10
  61. data/test/tc_relaxng.rb +4 -5
  62. data/test/tc_sax_parser.rb +9 -10
  63. data/test/tc_schema.rb +4 -5
  64. data/test/tc_traversal.rb +1 -2
  65. data/test/tc_writer.rb +1 -2
  66. data/test/tc_xinclude.rb +1 -2
  67. data/test/tc_xml.rb +1 -2
  68. data/test/tc_xpath.rb +8 -9
  69. data/test/tc_xpath_context.rb +3 -4
  70. data/test/tc_xpath_expression.rb +3 -4
  71. data/test/tc_xpointer.rb +1 -3
  72. data/test/test_helper.rb +3 -1
  73. data/test/test_suite.rb +0 -1
  74. metadata +90 -72
  75. data/test/etc_doc_to_s.rb +0 -21
  76. data/test/ets_doc_file.rb +0 -17
  77. data/test/ets_doc_to_s.rb +0 -23
  78. data/test/ets_gpx.rb +0 -28
  79. data/test/ets_node_gc.rb +0 -23
  80. data/test/ets_test.xml +0 -2
  81. data/test/ets_tsr.rb +0 -11
  82. data/test/model/kml_sample.xml +0 -915
  83. data/test/remove_test.rb +0 -9
  84. data/test/tc_gc.rb +0 -86
  85. data/test/tc_parser.rb.orig +0 -384
@@ -1,361 +1,360 @@
1
- /* Please see the LICENSE file for copyright and distribution information */
2
-
3
- #include "ruby_libxml.h"
4
- #include "ruby_xml_xpath_context.h"
5
- #include "ruby_xml_xpath_expression.h"
6
-
7
- #if RUBY_ST_H
8
- #include <ruby/st.h>
9
- #else
10
- #include <st.h>
11
- #endif
12
-
13
- /*
14
- * Document-class: LibXML::XML::XPath::Context
15
- *
16
- * The XML::XPath::Context class is used to evaluate XPath
17
- * expressions. Generally, you should not directly use this class,
18
- * but instead use the XML::Document#find and XML::Node#find methods.
19
- *
20
- * doc = XML::Document.string('<header>content</header>')
21
- * context = XPath::Context.new(doc)
22
- * context.node = doc.root
23
- * context.register_namespaces_from_node(doc.root)
24
- * nodes = context.find('/header')
25
- */
26
-
27
- VALUE cXMLXPathContext;
28
-
29
- static void rxml_xpath_context_free(xmlXPathContextPtr ctxt)
30
- {
31
- xmlXPathFreeContext(ctxt);
32
- }
33
-
34
- static void rxml_xpath_context_mark(xmlXPathContextPtr ctxt)
35
- {
36
- if (ctxt && ctxt->doc->_private)
37
- rb_gc_mark((VALUE) ctxt->doc->_private);
38
- }
39
-
40
- static VALUE rxml_xpath_context_alloc(VALUE klass)
41
- {
42
- return Data_Wrap_Struct(cXMLXPathContext, rxml_xpath_context_mark, rxml_xpath_context_free, NULL);
43
- }
44
-
45
- /* call-seq:
46
- * XPath::Context.new(doc) -> XPath::Context
47
- *
48
- * Creates a new XPath context for the specified document. The
49
- * context can then be used to evaluate an XPath expression.
50
- *
51
- * doc = XML::Document.string('<header><first>hi</first></header>')
52
- * context = XPath::Context.new(doc)
53
- * nodes = XPath::Object.new('//first', context)
54
- * nodes.length == 1
55
- */
56
- static VALUE rxml_xpath_context_initialize(VALUE self, VALUE document)
57
- {
58
- xmlDocPtr xdoc;
59
-
60
- if (rb_obj_is_kind_of(document, cXMLDocument) != Qtrue)
61
- {
62
- rb_raise(rb_eTypeError, "Supplied argument must be a document or node.");
63
- }
64
-
65
- Data_Get_Struct(document, xmlDoc, xdoc);
66
- DATA_PTR(self) = xmlXPathNewContext(xdoc);
67
-
68
- return self;
69
- }
70
-
71
- /*
72
- * call-seq:
73
- * context.doc -> document
74
- *
75
- * Obtain the XML::Document this node belongs to.
76
- */
77
- static VALUE rxml_xpath_context_doc(VALUE self)
78
- {
79
- xmlDocPtr xdoc = NULL;
80
- xmlXPathContextPtr ctxt;
81
- Data_Get_Struct(self, xmlXPathContext, ctxt);
82
-
83
- xdoc = ctxt->doc;
84
- return rxml_document_wrap(xdoc);
85
- }
86
-
87
- /*
88
- * call-seq:
89
- * context.register_namespace(prefix, uri) -> (true|false)
90
- *
91
- * Register the specified namespace URI with the specified prefix
92
- * in this context.
93
-
94
- * context.register_namespace('xi', 'http://www.w3.org/2001/XInclude')
95
- */
96
- static VALUE rxml_xpath_context_register_namespace(VALUE self, VALUE prefix, VALUE uri)
97
- {
98
- xmlXPathContextPtr ctxt;
99
- Data_Get_Struct(self, xmlXPathContext, ctxt);
100
-
101
- /* Prefix could be a symbol. */
102
- prefix = rb_obj_as_string(prefix);
103
-
104
- if (xmlXPathRegisterNs(ctxt, (xmlChar*) StringValuePtr(prefix),
105
- (xmlChar*) StringValuePtr(uri)) == 0)
106
- {
107
- return (Qtrue);
108
- }
109
- else
110
- {
111
- /* Should raise an exception, IMHO (whose?, why shouldnt it? -danj)*/
112
- rb_warning("register namespace failed");
113
- return (Qfalse);
114
- }
115
- }
116
-
117
- /* call-seq:
118
- * context.register_namespaces_from_node(node) -> self
119
- *
120
- * Helper method to read in namespaces defined on a node.
121
- *
122
- * doc = XML::Document.string('<header><first>hi</first></header>')
123
- * context = XPath::Context.new(doc)
124
- * context.register_namespaces_from_node(doc.root)
125
- */
126
- static VALUE rxml_xpath_context_register_namespaces_from_node(VALUE self,
127
- VALUE node)
128
- {
129
- xmlXPathContextPtr xctxt;
130
- xmlNodePtr xnode;
131
- xmlNsPtr *xnsArr;
132
-
133
- Data_Get_Struct(self, xmlXPathContext, xctxt);
134
-
135
- if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
136
- {
137
- xmlDocPtr xdoc;
138
- Data_Get_Struct(node, xmlDoc, xdoc);
139
- xnode = xmlDocGetRootElement(xdoc);
140
- }
141
- else if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
142
- {
143
- Data_Get_Struct(node, xmlNode, xnode);
144
- }
145
- else
146
- {
147
- rb_raise(rb_eTypeError, "The first argument must be a document or node.");
148
- }
149
-
150
- xnsArr = xmlGetNsList(xnode->doc, xnode);
151
-
152
- if (xnsArr)
153
- {
154
- xmlNsPtr xns = *xnsArr;
155
-
156
- while (xns)
157
- {
158
- /* If there is no prefix, then this is the default namespace.
159
- Skip it for now. */
160
- if (xns->prefix)
161
- {
162
- VALUE prefix = rxml_new_cstr((const char*)xns->prefix, xctxt->doc->encoding);
163
- VALUE uri = rxml_new_cstr((const char*)xns->href, xctxt->doc->encoding);
164
- rxml_xpath_context_register_namespace(self, prefix, uri);
165
- }
166
- xns = xns->next;
167
- }
168
- xmlFree(xnsArr);
169
- }
170
-
171
- return self;
172
- }
173
-
174
- static int iterate_ns_hash(VALUE prefix, VALUE uri, VALUE self)
175
- {
176
- rxml_xpath_context_register_namespace(self, prefix, uri);
177
- return ST_CONTINUE;
178
- }
179
-
180
- /*
181
- * call-seq:
182
- * context.register_namespaces(["prefix:uri"]) -> self
183
- *
184
- * Register the specified namespaces in this context. There are
185
- * three different forms that libxml accepts. These include
186
- * a string, an array of strings, or a hash table:
187
- *
188
- * context.register_namespaces('xi:http://www.w3.org/2001/XInclude')
189
- * context.register_namespaces(['xlink:http://www.w3.org/1999/xlink',
190
- * 'xi:http://www.w3.org/2001/XInclude')
191
- * context.register_namespaces('xlink' => 'http://www.w3.org/1999/xlink',
192
- * 'xi' => 'http://www.w3.org/2001/XInclude')
193
- */
194
- static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
195
- {
196
- char *cp;
197
- long i;
198
- VALUE rprefix, ruri;
199
- xmlXPathContextPtr xctxt;
200
-
201
- Data_Get_Struct(self, xmlXPathContext, xctxt);
202
-
203
- /* Need to loop through the 2nd argument and iterate through the
204
- * list of namespaces that we want to allow */
205
- switch (TYPE(nslist))
206
- {
207
- case T_STRING:
208
- cp = strchr(StringValuePtr(nslist), (int) ':');
209
- if (cp == NULL)
210
- {
211
- rprefix = nslist;
212
- ruri = Qnil;
213
- }
214
- else
215
- {
216
- rprefix = rb_str_new(StringValuePtr(nslist), (int) ((long) cp
217
- - (long) StringValuePtr(nslist)));
218
- ruri = rxml_new_cstr(&cp[1], xctxt->doc->encoding);
219
- }
220
- /* Should test the results of this */
221
- rxml_xpath_context_register_namespace(self, rprefix, ruri);
222
- break;
223
- case T_ARRAY:
224
- for (i = 0; i < RARRAY_LEN(nslist); i++)
225
- {
226
- rxml_xpath_context_register_namespaces(self, RARRAY_PTR(nslist)[i]);
227
- }
228
- break;
229
- case T_HASH:
230
- rb_hash_foreach(nslist, iterate_ns_hash, self);
231
- break;
232
- default:
233
- rb_raise(
234
- rb_eArgError,
235
- "Invalid argument type, only accept string, array of strings, or an array of arrays");
236
- }
237
- return self;
238
- }
239
-
240
- /*
241
- * call-seq:
242
- * context.node = node
243
- *
244
- * Set the current node used by the XPath engine
245
-
246
- * doc = XML::Document.string('<header><first>hi</first></header>')
247
- * context.node = doc.root.first
248
- */
249
- static VALUE rxml_xpath_context_node_set(VALUE self, VALUE node)
250
- {
251
- xmlXPathContextPtr xctxt;
252
- xmlNodePtr xnode;
253
-
254
- Data_Get_Struct(self, xmlXPathContext, xctxt);
255
- Data_Get_Struct(node, xmlNode, xnode);
256
- xctxt->node = xnode;
257
- return node;
258
- }
259
-
260
- /*
261
- * call-seq:
262
- * context.find("xpath") -> true|false|number|string|XML::XPath::Object
263
- *
264
- * Executes the provided xpath function. The result depends on the execution
265
- * of the xpath statement. It may be true, false, a number, a string or
266
- * a node set.
267
- */
268
- static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
269
- {
270
- xmlXPathContextPtr xctxt;
271
- xmlXPathObjectPtr xobject;
272
- xmlXPathCompExprPtr xcompexpr;
273
-
274
- Data_Get_Struct(self, xmlXPathContext, xctxt);
275
-
276
- if (TYPE(xpath_expr) == T_STRING)
277
- {
278
- VALUE expression = rb_check_string_type(xpath_expr);
279
- xobject = xmlXPathEval((xmlChar*) StringValueCStr(expression), xctxt);
280
- }
281
- else if (rb_obj_is_kind_of(xpath_expr, cXMLXPathExpression))
282
- {
283
- Data_Get_Struct(xpath_expr, xmlXPathCompExpr, xcompexpr);
284
- xobject = xmlXPathCompiledEval(xcompexpr, xctxt);
285
- }
286
- else
287
- {
288
- rb_raise(rb_eTypeError,
289
- "Argument should be an intance of a String or XPath::Expression");
290
- }
291
-
292
- return rxml_xpath_to_value(xctxt, xobject);
293
- }
294
-
295
- #if LIBXML_VERSION >= 20626
296
- /*
297
- * call-seq:
298
- * context.enable_cache(size = nil)
299
- *
300
- * Enables an XPath::Context's built-in cache. If the cache is
301
- * enabled then XPath objects will be cached internally for reuse.
302
- * The size parameter controls sets the maximum number of XPath objects
303
- * that will be cached per XPath object type (node-set, string, number,
304
- * boolean, and misc objects). Set size to nil to use the default
305
- * cache size of 100.
306
- */
307
- static VALUE
308
- rxml_xpath_context_enable_cache(int argc, VALUE *argv, VALUE self)
309
- {
310
- xmlXPathContextPtr xctxt;
311
- VALUE size;
312
- int value = -1;
313
-
314
- Data_Get_Struct(self, xmlXPathContext, xctxt);
315
-
316
- if (rb_scan_args(argc, argv, "01", &size) == 1)
317
- {
318
- value = NUM2INT(size);
319
- }
320
-
321
- if (xmlXPathContextSetCache(xctxt, 1, value, 0) == -1)
322
- rxml_raise(&xmlLastError);
323
-
324
- return self;
325
- }
326
-
327
- /*
328
- * call-seq:
329
- * context.disable_cache
330
- *
331
- * Disables an XPath::Context's built-in cache.
332
- */
333
- static VALUE
334
- rxml_xpath_context_disable_cache(VALUE self)
335
- {
336
- xmlXPathContextPtr xctxt;
337
- Data_Get_Struct(self, xmlXPathContext, xctxt);
338
-
339
- if (xmlXPathContextSetCache(xctxt, 0, 0, 0) == -1)
340
- rxml_raise(&xmlLastError);
341
-
342
- return self;
343
- }
344
- #endif
345
-
346
- void rxml_init_xpath_context(void)
347
- {
348
- cXMLXPathContext = rb_define_class_under(mXPath, "Context", rb_cObject);
349
- rb_define_alloc_func(cXMLXPathContext, rxml_xpath_context_alloc);
350
- rb_define_method(cXMLXPathContext, "doc", rxml_xpath_context_doc, 0);
351
- rb_define_method(cXMLXPathContext, "initialize", rxml_xpath_context_initialize, 1);
352
- rb_define_method(cXMLXPathContext, "register_namespaces", rxml_xpath_context_register_namespaces, 1);
353
- rb_define_method(cXMLXPathContext, "register_namespaces_from_node", rxml_xpath_context_register_namespaces_from_node, 1);
354
- rb_define_method(cXMLXPathContext, "register_namespace", rxml_xpath_context_register_namespace, 2);
355
- rb_define_method(cXMLXPathContext, "node=", rxml_xpath_context_node_set, 1);
356
- rb_define_method(cXMLXPathContext, "find", rxml_xpath_context_find, 1);
357
- #if LIBXML_VERSION >= 20626
358
- rb_define_method(cXMLXPathContext, "enable_cache", rxml_xpath_context_enable_cache, -1);
359
- rb_define_method(cXMLXPathContext, "disable_cache", rxml_xpath_context_disable_cache, 0);
360
- #endif
361
- }
1
+ /* Please see the LICENSE file for copyright and distribution information */
2
+
3
+ #include "ruby_libxml.h"
4
+ #include "ruby_xml_xpath_context.h"
5
+ #include "ruby_xml_xpath_expression.h"
6
+
7
+ #if RUBY_ST_H
8
+ #include <ruby/st.h>
9
+ #else
10
+ #include <st.h>
11
+ #endif
12
+
13
+ /*
14
+ * Document-class: LibXML::XML::XPath::Context
15
+ *
16
+ * The XML::XPath::Context class is used to evaluate XPath
17
+ * expressions. Generally, you should not directly use this class,
18
+ * but instead use the XML::Document#find and XML::Node#find methods.
19
+ *
20
+ * doc = XML::Document.string('<header>content</header>')
21
+ * context = XPath::Context.new(doc)
22
+ * context.node = doc.root
23
+ * context.register_namespaces_from_node(doc.root)
24
+ * nodes = context.find('/header')
25
+ */
26
+
27
+ VALUE cXMLXPathContext;
28
+
29
+ static void rxml_xpath_context_free(xmlXPathContextPtr ctxt)
30
+ {
31
+ xmlXPathFreeContext(ctxt);
32
+ }
33
+
34
+ static void rxml_xpath_context_mark(xmlXPathContextPtr ctxt)
35
+ {
36
+ VALUE value = rxml_lookup_doc(ctxt->doc);
37
+ rb_gc_mark(value);
38
+ }
39
+
40
+ static VALUE rxml_xpath_context_alloc(VALUE klass)
41
+ {
42
+ return Data_Wrap_Struct(cXMLXPathContext, rxml_xpath_context_mark, rxml_xpath_context_free, NULL);
43
+ }
44
+
45
+ /* call-seq:
46
+ * XPath::Context.new(doc) -> XPath::Context
47
+ *
48
+ * Creates a new XPath context for the specified document. The
49
+ * context can then be used to evaluate an XPath expression.
50
+ *
51
+ * doc = XML::Document.string('<header><first>hi</first></header>')
52
+ * context = XPath::Context.new(doc)
53
+ * nodes = XPath::Object.new('//first', context)
54
+ * nodes.length == 1
55
+ */
56
+ static VALUE rxml_xpath_context_initialize(VALUE self, VALUE document)
57
+ {
58
+ xmlDocPtr xdoc;
59
+
60
+ if (rb_obj_is_kind_of(document, cXMLDocument) != Qtrue)
61
+ {
62
+ rb_raise(rb_eTypeError, "Supplied argument must be a document or node.");
63
+ }
64
+
65
+ Data_Get_Struct(document, xmlDoc, xdoc);
66
+ DATA_PTR(self) = xmlXPathNewContext(xdoc);
67
+
68
+ return self;
69
+ }
70
+
71
+ /*
72
+ * call-seq:
73
+ * context.doc -> document
74
+ *
75
+ * Obtain the XML::Document this node belongs to.
76
+ */
77
+ static VALUE rxml_xpath_context_doc(VALUE self)
78
+ {
79
+ xmlDocPtr xdoc = NULL;
80
+ xmlXPathContextPtr ctxt;
81
+ Data_Get_Struct(self, xmlXPathContext, ctxt);
82
+
83
+ xdoc = ctxt->doc;
84
+ return rxml_document_wrap(xdoc);
85
+ }
86
+
87
+ /*
88
+ * call-seq:
89
+ * context.register_namespace(prefix, uri) -> (true|false)
90
+ *
91
+ * Register the specified namespace URI with the specified prefix
92
+ * in this context.
93
+
94
+ * context.register_namespace('xi', 'http://www.w3.org/2001/XInclude')
95
+ */
96
+ static VALUE rxml_xpath_context_register_namespace(VALUE self, VALUE prefix, VALUE uri)
97
+ {
98
+ xmlXPathContextPtr ctxt;
99
+ Data_Get_Struct(self, xmlXPathContext, ctxt);
100
+
101
+ /* Prefix could be a symbol. */
102
+ prefix = rb_obj_as_string(prefix);
103
+
104
+ if (xmlXPathRegisterNs(ctxt, (xmlChar*) StringValuePtr(prefix),
105
+ (xmlChar*) StringValuePtr(uri)) == 0)
106
+ {
107
+ return (Qtrue);
108
+ }
109
+ else
110
+ {
111
+ /* Should raise an exception, IMHO (whose?, why shouldnt it? -danj)*/
112
+ rb_warning("register namespace failed");
113
+ return (Qfalse);
114
+ }
115
+ }
116
+
117
+ /* call-seq:
118
+ * context.register_namespaces_from_node(node) -> self
119
+ *
120
+ * Helper method to read in namespaces defined on a node.
121
+ *
122
+ * doc = XML::Document.string('<header><first>hi</first></header>')
123
+ * context = XPath::Context.new(doc)
124
+ * context.register_namespaces_from_node(doc.root)
125
+ */
126
+ static VALUE rxml_xpath_context_register_namespaces_from_node(VALUE self,
127
+ VALUE node)
128
+ {
129
+ xmlXPathContextPtr xctxt;
130
+ xmlNodePtr xnode;
131
+ xmlNsPtr *xnsArr;
132
+
133
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
134
+
135
+ if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
136
+ {
137
+ xmlDocPtr xdoc;
138
+ Data_Get_Struct(node, xmlDoc, xdoc);
139
+ xnode = xmlDocGetRootElement(xdoc);
140
+ }
141
+ else if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
142
+ {
143
+ Data_Get_Struct(node, xmlNode, xnode);
144
+ }
145
+ else
146
+ {
147
+ rb_raise(rb_eTypeError, "The first argument must be a document or node.");
148
+ }
149
+
150
+ xnsArr = xmlGetNsList(xnode->doc, xnode);
151
+
152
+ if (xnsArr)
153
+ {
154
+ xmlNsPtr xns = *xnsArr;
155
+
156
+ while (xns)
157
+ {
158
+ /* If there is no prefix, then this is the default namespace.
159
+ Skip it for now. */
160
+ if (xns->prefix)
161
+ {
162
+ VALUE prefix = rxml_new_cstr(xns->prefix, xctxt->doc->encoding);
163
+ VALUE uri = rxml_new_cstr(xns->href, xctxt->doc->encoding);
164
+ rxml_xpath_context_register_namespace(self, prefix, uri);
165
+ }
166
+ xns = xns->next;
167
+ }
168
+ xmlFree(xnsArr);
169
+ }
170
+
171
+ return self;
172
+ }
173
+
174
+ static int iterate_ns_hash(VALUE prefix, VALUE uri, VALUE self)
175
+ {
176
+ rxml_xpath_context_register_namespace(self, prefix, uri);
177
+ return ST_CONTINUE;
178
+ }
179
+
180
+ /*
181
+ * call-seq:
182
+ * context.register_namespaces(["prefix:uri"]) -> self
183
+ *
184
+ * Register the specified namespaces in this context. There are
185
+ * three different forms that libxml accepts. These include
186
+ * a string, an array of strings, or a hash table:
187
+ *
188
+ * context.register_namespaces('xi:http://www.w3.org/2001/XInclude')
189
+ * context.register_namespaces(['xlink:http://www.w3.org/1999/xlink',
190
+ * 'xi:http://www.w3.org/2001/XInclude')
191
+ * context.register_namespaces('xlink' => 'http://www.w3.org/1999/xlink',
192
+ * 'xi' => 'http://www.w3.org/2001/XInclude')
193
+ */
194
+ static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
195
+ {
196
+ char *cp;
197
+ long i;
198
+ VALUE rprefix, ruri;
199
+ xmlXPathContextPtr xctxt;
200
+
201
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
202
+
203
+ /* Need to loop through the 2nd argument and iterate through the
204
+ * list of namespaces that we want to allow */
205
+ switch (TYPE(nslist))
206
+ {
207
+ case T_STRING:
208
+ cp = strchr(StringValuePtr(nslist), (int) ':');
209
+ if (cp == NULL)
210
+ {
211
+ rprefix = nslist;
212
+ ruri = Qnil;
213
+ }
214
+ else
215
+ {
216
+ rprefix = rb_str_new(StringValuePtr(nslist), (long) ((intptr_t) cp - (intptr_t)StringValuePtr(nslist)));
217
+ ruri = rxml_new_cstr((const xmlChar*)&cp[1], xctxt->doc->encoding);
218
+ }
219
+ /* Should test the results of this */
220
+ rxml_xpath_context_register_namespace(self, rprefix, ruri);
221
+ break;
222
+ case T_ARRAY:
223
+ for (i = 0; i < RARRAY_LEN(nslist); i++)
224
+ {
225
+ rxml_xpath_context_register_namespaces(self, RARRAY_PTR(nslist)[i]);
226
+ }
227
+ break;
228
+ case T_HASH:
229
+ rb_hash_foreach(nslist, iterate_ns_hash, self);
230
+ break;
231
+ default:
232
+ rb_raise(
233
+ rb_eArgError,
234
+ "Invalid argument type, only accept string, array of strings, or an array of arrays");
235
+ }
236
+ return self;
237
+ }
238
+
239
+ /*
240
+ * call-seq:
241
+ * context.node = node
242
+ *
243
+ * Set the current node used by the XPath engine
244
+
245
+ * doc = XML::Document.string('<header><first>hi</first></header>')
246
+ * context.node = doc.root.first
247
+ */
248
+ static VALUE rxml_xpath_context_node_set(VALUE self, VALUE node)
249
+ {
250
+ xmlXPathContextPtr xctxt;
251
+ xmlNodePtr xnode;
252
+
253
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
254
+ Data_Get_Struct(node, xmlNode, xnode);
255
+ xctxt->node = xnode;
256
+ return node;
257
+ }
258
+
259
+ /*
260
+ * call-seq:
261
+ * context.find("xpath") -> true|false|number|string|XML::XPath::Object
262
+ *
263
+ * Executes the provided xpath function. The result depends on the execution
264
+ * of the xpath statement. It may be true, false, a number, a string or
265
+ * a node set.
266
+ */
267
+ static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
268
+ {
269
+ xmlXPathContextPtr xctxt;
270
+ xmlXPathObjectPtr xobject;
271
+ xmlXPathCompExprPtr xcompexpr;
272
+
273
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
274
+
275
+ if (TYPE(xpath_expr) == T_STRING)
276
+ {
277
+ VALUE expression = rb_check_string_type(xpath_expr);
278
+ xobject = xmlXPathEval((xmlChar*) StringValueCStr(expression), xctxt);
279
+ }
280
+ else if (rb_obj_is_kind_of(xpath_expr, cXMLXPathExpression))
281
+ {
282
+ Data_Get_Struct(xpath_expr, xmlXPathCompExpr, xcompexpr);
283
+ xobject = xmlXPathCompiledEval(xcompexpr, xctxt);
284
+ }
285
+ else
286
+ {
287
+ rb_raise(rb_eTypeError,
288
+ "Argument should be an instance of a String or XPath::Expression");
289
+ }
290
+
291
+ return rxml_xpath_to_value(xctxt, xobject);
292
+ }
293
+
294
+ #if LIBXML_VERSION >= 20626
295
+ /*
296
+ * call-seq:
297
+ * context.enable_cache(size = nil)
298
+ *
299
+ * Enables an XPath::Context's built-in cache. If the cache is
300
+ * enabled then XPath objects will be cached internally for reuse.
301
+ * The size parameter controls sets the maximum number of XPath objects
302
+ * that will be cached per XPath object type (node-set, string, number,
303
+ * boolean, and misc objects). Set size to nil to use the default
304
+ * cache size of 100.
305
+ */
306
+ static VALUE
307
+ rxml_xpath_context_enable_cache(int argc, VALUE *argv, VALUE self)
308
+ {
309
+ xmlXPathContextPtr xctxt;
310
+ VALUE size;
311
+ int value = -1;
312
+
313
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
314
+
315
+ if (rb_scan_args(argc, argv, "01", &size) == 1)
316
+ {
317
+ value = NUM2INT(size);
318
+ }
319
+
320
+ if (xmlXPathContextSetCache(xctxt, 1, value, 0) == -1)
321
+ rxml_raise(&xmlLastError);
322
+
323
+ return self;
324
+ }
325
+
326
+ /*
327
+ * call-seq:
328
+ * context.disable_cache
329
+ *
330
+ * Disables an XPath::Context's built-in cache.
331
+ */
332
+ static VALUE
333
+ rxml_xpath_context_disable_cache(VALUE self)
334
+ {
335
+ xmlXPathContextPtr xctxt;
336
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
337
+
338
+ if (xmlXPathContextSetCache(xctxt, 0, 0, 0) == -1)
339
+ rxml_raise(&xmlLastError);
340
+
341
+ return self;
342
+ }
343
+ #endif
344
+
345
+ void rxml_init_xpath_context(void)
346
+ {
347
+ cXMLXPathContext = rb_define_class_under(mXPath, "Context", rb_cObject);
348
+ rb_define_alloc_func(cXMLXPathContext, rxml_xpath_context_alloc);
349
+ rb_define_method(cXMLXPathContext, "doc", rxml_xpath_context_doc, 0);
350
+ rb_define_method(cXMLXPathContext, "initialize", rxml_xpath_context_initialize, 1);
351
+ rb_define_method(cXMLXPathContext, "register_namespaces", rxml_xpath_context_register_namespaces, 1);
352
+ rb_define_method(cXMLXPathContext, "register_namespaces_from_node", rxml_xpath_context_register_namespaces_from_node, 1);
353
+ rb_define_method(cXMLXPathContext, "register_namespace", rxml_xpath_context_register_namespace, 2);
354
+ rb_define_method(cXMLXPathContext, "node=", rxml_xpath_context_node_set, 1);
355
+ rb_define_method(cXMLXPathContext, "find", rxml_xpath_context_find, 1);
356
+ #if LIBXML_VERSION >= 20626
357
+ rb_define_method(cXMLXPathContext, "enable_cache", rxml_xpath_context_enable_cache, -1);
358
+ rb_define_method(cXMLXPathContext, "disable_cache", rxml_xpath_context_disable_cache, 0);
359
+ #endif
360
+ }