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,9 +1,9 @@
1
1
  /* Don't nuke this block! It is used for automatically updating the
2
2
  * versions below. VERSION = string formatting, VERNUM = numbered
3
3
  * version for inline testing: increment both or none at all.*/
4
- #define RUBY_LIBXML_VERSION "0.9.9"
5
- #define RUBY_LIBXML_VERNUM 0
6
- #define RUBY_LIBXML_VER_MAJ 0
7
- #define RUBY_LIBXML_VER_MIN 9
8
- #define RUBY_LIBXML_VER_MIC 9
4
+ #define RUBY_LIBXML_VERSION "1.0.0"
5
+ #define RUBY_LIBXML_VERNUM 100
6
+ #define RUBY_LIBXML_VER_MAJ 1
7
+ #define RUBY_LIBXML_VER_MIN 0
8
+ #define RUBY_LIBXML_VER_MIC 0
9
9
  #define RUBY_LIBXML_VER_PATCH 0
@@ -1,387 +1,387 @@
1
- /* $Id: ruby_xml_xpath_context.c 799 2009-03-02 01:45:24Z cfis $ */
2
-
3
- /* Please see the LICENSE file for copyright and distribution information */
4
-
5
- #include "ruby_libxml.h"
6
- #include "ruby_xml_xpath_context.h"
7
- #include "ruby_xml_xpath_expression.h"
8
-
9
- #if RUBY_ST_H
10
- #include <ruby/st.h>
11
- #else
12
- #include <st.h>
13
- #endif
14
-
15
- /*
16
- * Document-class: LibXML::XML::XPath::Context
17
- *
18
- * The XML::XPath::Context class is used to evaluate XPath
19
- * expressions. Generally, you should not directly use this class,
20
- * but instead use the XML::Document#find and XML::Node#find methods.
21
- *
22
- * doc = XML::Document.string('<header>content</header>')
23
- * context = XPath::Context.new(doc)
24
- * context.node = doc.root
25
- * context.register_namespaces_from_node(doc.root)
26
- * nodes = context.find('/header')
27
- */
28
-
29
- VALUE cXMLXPathContext;
30
-
31
- static ID DOC_ATTRIBUTE;
32
-
33
- static void rxml_xpath_context_free(xmlXPathContextPtr ctxt)
34
- {
35
- xmlXPathFreeContext(ctxt);
36
- }
37
-
38
- static VALUE rxml_xpath_context_alloc(VALUE klass)
39
- {
40
- return Data_Wrap_Struct(cXMLXPathContext, NULL, rxml_xpath_context_free, NULL);
41
- }
42
-
43
- /* call-seq:
44
- * XPath::Context.new(node) -> XPath::Context
45
- *
46
- * Creates a new XPath context for the specified document. The
47
- * context can then be used to evaluate an XPath expression.
48
- *
49
- * doc = XML::Document.string('<header><first>hi</first></header>')
50
- * context = XPath::Context.new(doc)
51
- * nodes = XPath::Object.new('//first', context)
52
- * nodes.length == 1
53
- */
54
- static VALUE rxml_xpath_context_initialize(VALUE self, VALUE node)
55
- {
56
- xmlDocPtr xdoc;
57
- VALUE document;
58
-
59
- if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
60
- {
61
- document = rb_funcall(node, rb_intern("doc"), 0);
62
- if (NIL_P(document))
63
- rb_raise(rb_eTypeError, "Supplied node must belong to a document.");
64
- }
65
- else if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
66
- {
67
- document = node;
68
- }
69
- else
70
- {
71
- rb_raise(rb_eTypeError, "Supplied argument must be a document or node.");
72
- }
73
-
74
- Data_Get_Struct(document, xmlDoc, xdoc);
75
- DATA_PTR(self) = xmlXPathNewContext(xdoc);
76
-
77
- /* Save the doc as an attribute, this will expose it to Ruby's GC. */
78
- rb_ivar_set(self, DOC_ATTRIBUTE, document);
79
-
80
- return self;
81
- }
82
-
83
- /*
84
- * call-seq:
85
- * context.register_namespace(prefix, uri) -> (true|false)
86
- *
87
- * Register the specified namespace URI with the specified prefix
88
- * in this context.
89
-
90
- * context.register_namespace('xi', 'http://www.w3.org/2001/XInclude')
91
- */
92
- static VALUE rxml_xpath_context_register_namespace(VALUE self, VALUE prefix, VALUE uri)
93
- {
94
- xmlXPathContextPtr ctxt;
95
- Data_Get_Struct(self, xmlXPathContext, ctxt);
96
-
97
- /* Prefix could be a symbol. */
98
- prefix = rb_obj_as_string(prefix);
99
-
100
- if (xmlXPathRegisterNs(ctxt, (xmlChar*) StringValuePtr(prefix),
101
- (xmlChar*) StringValuePtr(uri)) == 0)
102
- {
103
- return (Qtrue);
104
- }
105
- else
106
- {
107
- /* Should raise an exception, IMHO (whose?, why shouldnt it? -danj)*/
108
- rb_warning("register namespace failed");
109
- return (Qfalse);
110
- }
111
- }
112
-
113
- /* call-seq:
114
- * context.register_namespaces_from_node(node) -> self
115
- *
116
- * Helper method to read in namespaces defined on a node.
117
- *
118
- * doc = XML::Document.string('<header><first>hi</first></header>')
119
- * context = XPath::Context.new(doc)
120
- * context.register_namespaces_from_node(doc.root)
121
- */
122
- static VALUE rxml_xpath_context_register_namespaces_from_node(VALUE self,
123
- VALUE node)
124
- {
125
- xmlXPathContextPtr xctxt;
126
- xmlNodePtr xnode;
127
- xmlNsPtr *xnsArr;
128
-
129
- Data_Get_Struct(self, xmlXPathContext, xctxt);
130
-
131
- if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
132
- {
133
- xmlDocPtr xdoc;
134
- Data_Get_Struct(node, xmlDoc, xdoc);
135
- xnode = xmlDocGetRootElement(xdoc);
136
- }
137
- else if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
138
- {
139
- Data_Get_Struct(node, xmlNode, xnode);
140
- }
141
- else
142
- {
143
- rb_raise(rb_eTypeError, "The first argument must be a document or node.");
144
- }
145
-
146
- xnsArr = xmlGetNsList(xnode->doc, xnode);
147
-
148
- if (xnsArr)
149
- {
150
- xmlNsPtr xns = *xnsArr;
151
-
152
- while (xns)
153
- {
154
- /* If there is no prefix, then this is the default namespace.
155
- Skip it for now. */
156
- if (xns->prefix)
157
- {
158
- VALUE prefix = rb_str_new2((const char*)xns->prefix);
159
- VALUE uri = rb_str_new2((const char*)xns->href);
160
- rxml_xpath_context_register_namespace(self, prefix, uri);
161
- }
162
- xns = xns->next;
163
- }
164
- xmlFree(xnsArr);
165
- }
166
-
167
- return self;
168
- }
169
-
170
- static int iterate_ns_hash(st_data_t prefix, st_data_t uri, st_data_t self)
171
- {
172
- rxml_xpath_context_register_namespace(self, prefix, uri);
173
- return ST_CONTINUE;
174
- }
175
-
176
- /*
177
- * call-seq:
178
- * context.register_namespaces(["prefix:uri"]) -> self
179
- *
180
- * Register the specified namespaces in this context. There are
181
- * three different forms that libxml accepts. These include
182
- * a string, an array of strings, or a hash table:
183
- *
184
- * context.register_namespaces('xi:http://www.w3.org/2001/XInclude')
185
- * context.register_namespaces(['xlink:http://www.w3.org/1999/xlink',
186
- * 'xi:http://www.w3.org/2001/XInclude')
187
- * context.register_namespaces('xlink' => 'http://www.w3.org/1999/xlink',
188
- * 'xi' => 'http://www.w3.org/2001/XInclude')
189
- */
190
- static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
191
- {
192
- char *cp;
193
- long i;
194
- VALUE rprefix, ruri;
195
-
196
- /* Need to loop through the 2nd argument and iterate through the
197
- * list of namespaces that we want to allow */
198
- switch (TYPE(nslist))
199
- {
200
- case T_STRING:
201
- cp = strchr(StringValuePtr(nslist), (int) ':');
202
- if (cp == NULL)
203
- {
204
- rprefix = nslist;
205
- ruri = Qnil;
206
- }
207
- else
208
- {
209
- rprefix = rb_str_new(StringValuePtr(nslist), (int) ((long) cp
210
- - (long) StringValuePtr(nslist)));
211
- ruri = rb_str_new2(&cp[1]);
212
- }
213
- /* Should test the results of this */
214
- rxml_xpath_context_register_namespace(self, rprefix, ruri);
215
- break;
216
- case T_ARRAY:
217
- for (i = 0; i < RARRAY_LEN(nslist); i++)
218
- {
219
- rxml_xpath_context_register_namespaces(self, RARRAY_PTR(nslist)[i]);
220
- }
221
- break;
222
- case T_HASH:
223
- st_foreach(RHASH_TBL(nslist), iterate_ns_hash, self);
224
- break;
225
- default:
226
- rb_raise(
227
- rb_eArgError,
228
- "Invalid argument type, only accept string, array of strings, or an array of arrays");
229
- }
230
- return self;
231
- }
232
-
233
- /*
234
- * call-seq:
235
- * context.node = node
236
- *
237
- * Set the current node used by the XPath engine
238
-
239
- * doc = XML::Document.string('<header><first>hi</first></header>')
240
- * context.node = doc.root.first
241
- */
242
- static VALUE rxml_xpath_context_node_set(VALUE self, VALUE node)
243
- {
244
- xmlXPathContextPtr xctxt;
245
- xmlNodePtr xnode;
246
-
247
- Data_Get_Struct(self, xmlXPathContext, xctxt);
248
- Data_Get_Struct(node, xmlNode, xnode);
249
- xctxt->node = xnode;
250
- return node;
251
- }
252
-
253
- /*
254
- * call-seq:
255
- * context.find("xpath") -> true|false|number|string|XML::XPath::Object
256
- *
257
- * Executes the provided xpath function. The result depends on the execution
258
- * of the xpath statement. It may be true, false, a number, a string or
259
- * a node set.
260
- */
261
- static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
262
- {
263
- xmlXPathContextPtr xctxt;
264
- xmlXPathObjectPtr xobject;
265
- xmlXPathCompExprPtr xcompexpr;
266
- VALUE result;
267
-
268
- Data_Get_Struct(self, xmlXPathContext, xctxt);
269
-
270
- if (TYPE(xpath_expr) == T_STRING)
271
- {
272
- VALUE expression = rb_check_string_type(xpath_expr);
273
- xobject = xmlXPathEval((xmlChar*) StringValueCStr(expression), xctxt);
274
- }
275
- else if (rb_obj_is_kind_of(xpath_expr, cXMLXPathExpression))
276
- {
277
- Data_Get_Struct(xpath_expr, xmlXPathCompExpr, xcompexpr);
278
- xobject = xmlXPathCompiledEval(xcompexpr, xctxt);
279
- }
280
- else
281
- {
282
- rb_raise(rb_eTypeError,
283
- "Argument should be an intance of a String or XPath::Expression");
284
- }
285
-
286
- if (xobject == NULL)
287
- {
288
- /* xmlLastError is different than xctxt->lastError. Use
289
- xmlLastError since it has the message set while xctxt->lastError
290
- does not. */
291
- xmlErrorPtr xerror = xmlGetLastError();
292
- rxml_raise(xerror);
293
- }
294
-
295
- switch (xobject->type)
296
- {
297
- case XPATH_NODESET:
298
- result = rxml_xpath_object_wrap(xctxt->doc, xobject);
299
- break;
300
- case XPATH_BOOLEAN:
301
- result = (xobject->boolval != 0) ? Qtrue : Qfalse;
302
- xmlXPathFreeObject(xobject);
303
- break;
304
- case XPATH_NUMBER:
305
- result = rb_float_new(xobject->floatval);
306
- xmlXPathFreeObject(xobject);
307
- break;
308
- case XPATH_STRING:
309
- result = rb_str_new2((const char*)xobject->stringval);
310
- xmlXPathFreeObject(xobject);
311
- break;
312
- default:
313
- result = Qnil;
314
- xmlXPathFreeObject(xobject);
315
- }
316
- return result;
317
- }
318
-
319
- #if LIBXML_VERSION >= 20626
320
- /*
321
- * call-seq:
322
- * context.enable_cache(size = nil)
323
- *
324
- * Enables an XPath::Context's built-in cache. If the cache is
325
- * enabled then XPath objects will be cached internally for reuse.
326
- * The size parameter controls sets the maximum number of XPath objects
327
- * that will be cached per XPath object type (node-set, string, number,
328
- * boolean, and misc objects). Set size to nil to use the default
329
- * cache size of 100.
330
- */
331
- static VALUE
332
- rxml_xpath_context_enable_cache(int argc, VALUE *argv, VALUE self)
333
- {
334
- xmlXPathContextPtr xctxt;
335
- VALUE size;
336
- int value = -1;
337
-
338
- Data_Get_Struct(self, xmlXPathContext, xctxt);
339
-
340
- if (rb_scan_args(argc, argv, "01", &size) == 1)
341
- {
342
- value = NUM2INT(size);
343
- }
344
-
345
- if (xmlXPathContextSetCache(xctxt, 1, value, 0) == -1)
346
- rxml_raise(&xmlLastError);
347
-
348
- return self;
349
- }
350
-
351
- /*
352
- * call-seq:
353
- * context.disable_cache
354
- *
355
- * Disables an XPath::Context's built-in cache.
356
- */
357
- static VALUE
358
- rxml_xpath_context_disable_cache(VALUE self)
359
- {
360
- xmlXPathContextPtr xctxt;
361
- Data_Get_Struct(self, xmlXPathContext, xctxt);
362
-
363
- if (xmlXPathContextSetCache(xctxt, 0, 0, 0) == -1)
364
- rxml_raise(&xmlLastError);
365
-
366
- return self;
367
- }
368
- #endif
369
-
370
- void rxml_init_xpath_context(void)
371
- {
372
- DOC_ATTRIBUTE = rb_intern("@doc");
373
-
374
- cXMLXPathContext = rb_define_class_under(mXPath, "Context", rb_cObject);
375
- rb_define_alloc_func(cXMLXPathContext, rxml_xpath_context_alloc);
376
- rb_define_attr(cXMLXPathContext, "doc", 1, 0);
377
- rb_define_method(cXMLXPathContext, "initialize", rxml_xpath_context_initialize, 1);
378
- rb_define_method(cXMLXPathContext, "register_namespaces", rxml_xpath_context_register_namespaces, 1);
379
- rb_define_method(cXMLXPathContext, "register_namespaces_from_node", rxml_xpath_context_register_namespaces_from_node, 1);
380
- rb_define_method(cXMLXPathContext, "register_namespace", rxml_xpath_context_register_namespace, 2);
381
- rb_define_method(cXMLXPathContext, "node=", rxml_xpath_context_node_set, 1);
382
- rb_define_method(cXMLXPathContext, "find", rxml_xpath_context_find, 1);
383
- #if LIBXML_VERSION >= 20626
384
- rb_define_method(cXMLXPathContext, "enable_cache", rxml_xpath_context_enable_cache, -1);
385
- rb_define_method(cXMLXPathContext, "disable_cache", rxml_xpath_context_disable_cache, 0);
386
- #endif
387
- }
1
+ /* $Id: ruby_xml_xpath_context.c 799 2009-03-02 01:45:24Z cfis $ */
2
+
3
+ /* Please see the LICENSE file for copyright and distribution information */
4
+
5
+ #include "ruby_libxml.h"
6
+ #include "ruby_xml_xpath_context.h"
7
+ #include "ruby_xml_xpath_expression.h"
8
+
9
+ #if RUBY_ST_H
10
+ #include <ruby/st.h>
11
+ #else
12
+ #include <st.h>
13
+ #endif
14
+
15
+ /*
16
+ * Document-class: LibXML::XML::XPath::Context
17
+ *
18
+ * The XML::XPath::Context class is used to evaluate XPath
19
+ * expressions. Generally, you should not directly use this class,
20
+ * but instead use the XML::Document#find and XML::Node#find methods.
21
+ *
22
+ * doc = XML::Document.string('<header>content</header>')
23
+ * context = XPath::Context.new(doc)
24
+ * context.node = doc.root
25
+ * context.register_namespaces_from_node(doc.root)
26
+ * nodes = context.find('/header')
27
+ */
28
+
29
+ VALUE cXMLXPathContext;
30
+
31
+ static ID DOC_ATTRIBUTE;
32
+
33
+ static void rxml_xpath_context_free(xmlXPathContextPtr ctxt)
34
+ {
35
+ xmlXPathFreeContext(ctxt);
36
+ }
37
+
38
+ static VALUE rxml_xpath_context_alloc(VALUE klass)
39
+ {
40
+ return Data_Wrap_Struct(cXMLXPathContext, NULL, rxml_xpath_context_free, NULL);
41
+ }
42
+
43
+ /* call-seq:
44
+ * XPath::Context.new(node) -> XPath::Context
45
+ *
46
+ * Creates a new XPath context for the specified document. The
47
+ * context can then be used to evaluate an XPath expression.
48
+ *
49
+ * doc = XML::Document.string('<header><first>hi</first></header>')
50
+ * context = XPath::Context.new(doc)
51
+ * nodes = XPath::Object.new('//first', context)
52
+ * nodes.length == 1
53
+ */
54
+ static VALUE rxml_xpath_context_initialize(VALUE self, VALUE node)
55
+ {
56
+ xmlDocPtr xdoc;
57
+ VALUE document;
58
+
59
+ if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
60
+ {
61
+ document = rb_funcall(node, rb_intern("doc"), 0);
62
+ if (NIL_P(document))
63
+ rb_raise(rb_eTypeError, "Supplied node must belong to a document.");
64
+ }
65
+ else if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
66
+ {
67
+ document = node;
68
+ }
69
+ else
70
+ {
71
+ rb_raise(rb_eTypeError, "Supplied argument must be a document or node.");
72
+ }
73
+
74
+ Data_Get_Struct(document, xmlDoc, xdoc);
75
+ DATA_PTR(self) = xmlXPathNewContext(xdoc);
76
+
77
+ /* Save the doc as an attribute, this will expose it to Ruby's GC. */
78
+ rb_ivar_set(self, DOC_ATTRIBUTE, document);
79
+
80
+ return self;
81
+ }
82
+
83
+ /*
84
+ * call-seq:
85
+ * context.register_namespace(prefix, uri) -> (true|false)
86
+ *
87
+ * Register the specified namespace URI with the specified prefix
88
+ * in this context.
89
+
90
+ * context.register_namespace('xi', 'http://www.w3.org/2001/XInclude')
91
+ */
92
+ static VALUE rxml_xpath_context_register_namespace(VALUE self, VALUE prefix, VALUE uri)
93
+ {
94
+ xmlXPathContextPtr ctxt;
95
+ Data_Get_Struct(self, xmlXPathContext, ctxt);
96
+
97
+ /* Prefix could be a symbol. */
98
+ prefix = rb_obj_as_string(prefix);
99
+
100
+ if (xmlXPathRegisterNs(ctxt, (xmlChar*) StringValuePtr(prefix),
101
+ (xmlChar*) StringValuePtr(uri)) == 0)
102
+ {
103
+ return (Qtrue);
104
+ }
105
+ else
106
+ {
107
+ /* Should raise an exception, IMHO (whose?, why shouldnt it? -danj)*/
108
+ rb_warning("register namespace failed");
109
+ return (Qfalse);
110
+ }
111
+ }
112
+
113
+ /* call-seq:
114
+ * context.register_namespaces_from_node(node) -> self
115
+ *
116
+ * Helper method to read in namespaces defined on a node.
117
+ *
118
+ * doc = XML::Document.string('<header><first>hi</first></header>')
119
+ * context = XPath::Context.new(doc)
120
+ * context.register_namespaces_from_node(doc.root)
121
+ */
122
+ static VALUE rxml_xpath_context_register_namespaces_from_node(VALUE self,
123
+ VALUE node)
124
+ {
125
+ xmlXPathContextPtr xctxt;
126
+ xmlNodePtr xnode;
127
+ xmlNsPtr *xnsArr;
128
+
129
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
130
+
131
+ if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
132
+ {
133
+ xmlDocPtr xdoc;
134
+ Data_Get_Struct(node, xmlDoc, xdoc);
135
+ xnode = xmlDocGetRootElement(xdoc);
136
+ }
137
+ else if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
138
+ {
139
+ Data_Get_Struct(node, xmlNode, xnode);
140
+ }
141
+ else
142
+ {
143
+ rb_raise(rb_eTypeError, "The first argument must be a document or node.");
144
+ }
145
+
146
+ xnsArr = xmlGetNsList(xnode->doc, xnode);
147
+
148
+ if (xnsArr)
149
+ {
150
+ xmlNsPtr xns = *xnsArr;
151
+
152
+ while (xns)
153
+ {
154
+ /* If there is no prefix, then this is the default namespace.
155
+ Skip it for now. */
156
+ if (xns->prefix)
157
+ {
158
+ VALUE prefix = rb_str_new2((const char*)xns->prefix);
159
+ VALUE uri = rb_str_new2((const char*)xns->href);
160
+ rxml_xpath_context_register_namespace(self, prefix, uri);
161
+ }
162
+ xns = xns->next;
163
+ }
164
+ xmlFree(xnsArr);
165
+ }
166
+
167
+ return self;
168
+ }
169
+
170
+ static int iterate_ns_hash(st_data_t prefix, st_data_t uri, st_data_t self)
171
+ {
172
+ rxml_xpath_context_register_namespace(self, prefix, uri);
173
+ return ST_CONTINUE;
174
+ }
175
+
176
+ /*
177
+ * call-seq:
178
+ * context.register_namespaces(["prefix:uri"]) -> self
179
+ *
180
+ * Register the specified namespaces in this context. There are
181
+ * three different forms that libxml accepts. These include
182
+ * a string, an array of strings, or a hash table:
183
+ *
184
+ * context.register_namespaces('xi:http://www.w3.org/2001/XInclude')
185
+ * context.register_namespaces(['xlink:http://www.w3.org/1999/xlink',
186
+ * 'xi:http://www.w3.org/2001/XInclude')
187
+ * context.register_namespaces('xlink' => 'http://www.w3.org/1999/xlink',
188
+ * 'xi' => 'http://www.w3.org/2001/XInclude')
189
+ */
190
+ static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
191
+ {
192
+ char *cp;
193
+ long i;
194
+ VALUE rprefix, ruri;
195
+
196
+ /* Need to loop through the 2nd argument and iterate through the
197
+ * list of namespaces that we want to allow */
198
+ switch (TYPE(nslist))
199
+ {
200
+ case T_STRING:
201
+ cp = strchr(StringValuePtr(nslist), (int) ':');
202
+ if (cp == NULL)
203
+ {
204
+ rprefix = nslist;
205
+ ruri = Qnil;
206
+ }
207
+ else
208
+ {
209
+ rprefix = rb_str_new(StringValuePtr(nslist), (int) ((long) cp
210
+ - (long) StringValuePtr(nslist)));
211
+ ruri = rb_str_new2(&cp[1]);
212
+ }
213
+ /* Should test the results of this */
214
+ rxml_xpath_context_register_namespace(self, rprefix, ruri);
215
+ break;
216
+ case T_ARRAY:
217
+ for (i = 0; i < RARRAY_LEN(nslist); i++)
218
+ {
219
+ rxml_xpath_context_register_namespaces(self, RARRAY_PTR(nslist)[i]);
220
+ }
221
+ break;
222
+ case T_HASH:
223
+ st_foreach(RHASH_TBL(nslist), iterate_ns_hash, self);
224
+ break;
225
+ default:
226
+ rb_raise(
227
+ rb_eArgError,
228
+ "Invalid argument type, only accept string, array of strings, or an array of arrays");
229
+ }
230
+ return self;
231
+ }
232
+
233
+ /*
234
+ * call-seq:
235
+ * context.node = node
236
+ *
237
+ * Set the current node used by the XPath engine
238
+
239
+ * doc = XML::Document.string('<header><first>hi</first></header>')
240
+ * context.node = doc.root.first
241
+ */
242
+ static VALUE rxml_xpath_context_node_set(VALUE self, VALUE node)
243
+ {
244
+ xmlXPathContextPtr xctxt;
245
+ xmlNodePtr xnode;
246
+
247
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
248
+ Data_Get_Struct(node, xmlNode, xnode);
249
+ xctxt->node = xnode;
250
+ return node;
251
+ }
252
+
253
+ /*
254
+ * call-seq:
255
+ * context.find("xpath") -> true|false|number|string|XML::XPath::Object
256
+ *
257
+ * Executes the provided xpath function. The result depends on the execution
258
+ * of the xpath statement. It may be true, false, a number, a string or
259
+ * a node set.
260
+ */
261
+ static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
262
+ {
263
+ xmlXPathContextPtr xctxt;
264
+ xmlXPathObjectPtr xobject;
265
+ xmlXPathCompExprPtr xcompexpr;
266
+ VALUE result;
267
+
268
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
269
+
270
+ if (TYPE(xpath_expr) == T_STRING)
271
+ {
272
+ VALUE expression = rb_check_string_type(xpath_expr);
273
+ xobject = xmlXPathEval((xmlChar*) StringValueCStr(expression), xctxt);
274
+ }
275
+ else if (rb_obj_is_kind_of(xpath_expr, cXMLXPathExpression))
276
+ {
277
+ Data_Get_Struct(xpath_expr, xmlXPathCompExpr, xcompexpr);
278
+ xobject = xmlXPathCompiledEval(xcompexpr, xctxt);
279
+ }
280
+ else
281
+ {
282
+ rb_raise(rb_eTypeError,
283
+ "Argument should be an intance of a String or XPath::Expression");
284
+ }
285
+
286
+ if (xobject == NULL)
287
+ {
288
+ /* xmlLastError is different than xctxt->lastError. Use
289
+ xmlLastError since it has the message set while xctxt->lastError
290
+ does not. */
291
+ xmlErrorPtr xerror = xmlGetLastError();
292
+ rxml_raise(xerror);
293
+ }
294
+
295
+ switch (xobject->type)
296
+ {
297
+ case XPATH_NODESET:
298
+ result = rxml_xpath_object_wrap(xctxt->doc, xobject);
299
+ break;
300
+ case XPATH_BOOLEAN:
301
+ result = (xobject->boolval != 0) ? Qtrue : Qfalse;
302
+ xmlXPathFreeObject(xobject);
303
+ break;
304
+ case XPATH_NUMBER:
305
+ result = rb_float_new(xobject->floatval);
306
+ xmlXPathFreeObject(xobject);
307
+ break;
308
+ case XPATH_STRING:
309
+ result = rb_str_new2((const char*)xobject->stringval);
310
+ xmlXPathFreeObject(xobject);
311
+ break;
312
+ default:
313
+ result = Qnil;
314
+ xmlXPathFreeObject(xobject);
315
+ }
316
+ return result;
317
+ }
318
+
319
+ #if LIBXML_VERSION >= 20626
320
+ /*
321
+ * call-seq:
322
+ * context.enable_cache(size = nil)
323
+ *
324
+ * Enables an XPath::Context's built-in cache. If the cache is
325
+ * enabled then XPath objects will be cached internally for reuse.
326
+ * The size parameter controls sets the maximum number of XPath objects
327
+ * that will be cached per XPath object type (node-set, string, number,
328
+ * boolean, and misc objects). Set size to nil to use the default
329
+ * cache size of 100.
330
+ */
331
+ static VALUE
332
+ rxml_xpath_context_enable_cache(int argc, VALUE *argv, VALUE self)
333
+ {
334
+ xmlXPathContextPtr xctxt;
335
+ VALUE size;
336
+ int value = -1;
337
+
338
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
339
+
340
+ if (rb_scan_args(argc, argv, "01", &size) == 1)
341
+ {
342
+ value = NUM2INT(size);
343
+ }
344
+
345
+ if (xmlXPathContextSetCache(xctxt, 1, value, 0) == -1)
346
+ rxml_raise(&xmlLastError);
347
+
348
+ return self;
349
+ }
350
+
351
+ /*
352
+ * call-seq:
353
+ * context.disable_cache
354
+ *
355
+ * Disables an XPath::Context's built-in cache.
356
+ */
357
+ static VALUE
358
+ rxml_xpath_context_disable_cache(VALUE self)
359
+ {
360
+ xmlXPathContextPtr xctxt;
361
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
362
+
363
+ if (xmlXPathContextSetCache(xctxt, 0, 0, 0) == -1)
364
+ rxml_raise(&xmlLastError);
365
+
366
+ return self;
367
+ }
368
+ #endif
369
+
370
+ void rxml_init_xpath_context(void)
371
+ {
372
+ DOC_ATTRIBUTE = rb_intern("@doc");
373
+
374
+ cXMLXPathContext = rb_define_class_under(mXPath, "Context", rb_cObject);
375
+ rb_define_alloc_func(cXMLXPathContext, rxml_xpath_context_alloc);
376
+ rb_define_attr(cXMLXPathContext, "doc", 1, 0);
377
+ rb_define_method(cXMLXPathContext, "initialize", rxml_xpath_context_initialize, 1);
378
+ rb_define_method(cXMLXPathContext, "register_namespaces", rxml_xpath_context_register_namespaces, 1);
379
+ rb_define_method(cXMLXPathContext, "register_namespaces_from_node", rxml_xpath_context_register_namespaces_from_node, 1);
380
+ rb_define_method(cXMLXPathContext, "register_namespace", rxml_xpath_context_register_namespace, 2);
381
+ rb_define_method(cXMLXPathContext, "node=", rxml_xpath_context_node_set, 1);
382
+ rb_define_method(cXMLXPathContext, "find", rxml_xpath_context_find, 1);
383
+ #if LIBXML_VERSION >= 20626
384
+ rb_define_method(cXMLXPathContext, "enable_cache", rxml_xpath_context_enable_cache, -1);
385
+ rb_define_method(cXMLXPathContext, "disable_cache", rxml_xpath_context_disable_cache, 0);
386
+ #endif
387
+ }