libxml-ruby 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,322 @@
1
+ /* $Id: ruby_xml_sax_parser.c 682 2008-12-09 05:31:13Z cfis $ */
2
+
3
+ /* Please see the LICENSE file for copyright and distribution information */
4
+
5
+ #include "ruby_libxml.h"
6
+ #include "ruby_xml_sax2_handler.h"
7
+
8
+
9
+ VALUE cbidOnCdataBlock;
10
+ VALUE cbidOnCharacters;
11
+ VALUE cbidOnComment;
12
+ VALUE cbidOnEndDocument;
13
+ VALUE cbidOnEndElement;
14
+ VALUE cbidOnEndElementNs;
15
+ VALUE cbidOnExternalSubset;
16
+ VALUE cbidOnHasExternalSubset;
17
+ VALUE cbidOnHasInternalSubset;
18
+ VALUE cbidOnInternalSubset;
19
+ VALUE cbidOnIsStandalone;
20
+ VALUE cbidOnError;
21
+ VALUE cbidOnProcessingInstruction;
22
+ VALUE cbidOnReference;
23
+ VALUE cbidOnStartElement;
24
+ VALUE cbidOnStartElementNs;
25
+ VALUE cbidOnStartDocument;
26
+
27
+ /* ====== Callbacks =========== */
28
+ static void cdata_block_callback(void *ctx,
29
+ const char *value, int len)
30
+ {
31
+ VALUE handler = (VALUE) ctx;
32
+
33
+ if (handler != Qnil)
34
+ {
35
+ rb_funcall(handler, cbidOnCdataBlock,1,rb_str_new(value, len));
36
+ }
37
+ }
38
+
39
+ static void characters_callback(void *ctx,
40
+ const char *chars, int len)
41
+ {
42
+ VALUE handler = (VALUE) ctx;
43
+
44
+ if (handler != Qnil)
45
+ {
46
+ VALUE rchars = rb_str_new(chars, len);
47
+ rb_funcall(handler, cbidOnCharacters, 1, rchars);
48
+ }
49
+ }
50
+
51
+ static void comment_callback(void *ctx,
52
+ const char *msg)
53
+ {
54
+ VALUE handler = (VALUE) ctx;
55
+
56
+ if (handler != Qnil)
57
+ {
58
+ rb_funcall(handler, cbidOnComment,1,rb_str_new2(msg));
59
+ }
60
+ }
61
+
62
+ static void end_document_callback(void *ctx)
63
+ {
64
+ VALUE handler = (VALUE) ctx;
65
+
66
+ if (handler != Qnil)
67
+ {
68
+ rb_funcall(handler, cbidOnEndDocument, 0);
69
+ }
70
+ }
71
+
72
+ static void end_element_ns_callback(void *ctx,
73
+ const xmlChar *xlocalname, const xmlChar *xprefix, const xmlChar *xURI)
74
+ {
75
+ VALUE handler = (VALUE) ctx;
76
+
77
+ if (handler == Qnil)
78
+ return;
79
+
80
+ /* Call end element for old-times sake */
81
+ if (rb_respond_to(handler, cbidOnEndElement))
82
+ {
83
+ VALUE name;
84
+ if (xprefix)
85
+ {
86
+ name = rb_str_new2(xprefix);
87
+ rb_str_cat2(name, ":");
88
+ rb_str_cat2(name, xlocalname);
89
+ }
90
+ else
91
+ {
92
+ name = rb_str_new2(xlocalname);
93
+ }
94
+ rb_funcall(handler, cbidOnEndElement, 1, name);
95
+ }
96
+
97
+ rb_funcall(handler, cbidOnEndElementNs, 3,
98
+ rb_str_new2(xlocalname),
99
+ xprefix ? rb_str_new2(xprefix) : Qnil,
100
+ xURI ? rb_str_new2(xURI) : Qnil);
101
+ }
102
+
103
+ static void external_subset_callback(void *ctx, const char *name, const char *extid, const char *sysid)
104
+ {
105
+ VALUE handler = (VALUE) ctx;
106
+
107
+ if (handler != Qnil)
108
+ {
109
+ VALUE rname = name ? rb_str_new2(name) : Qnil;
110
+ VALUE rextid = extid ? rb_str_new2(extid) : Qnil;
111
+ VALUE rsysid = sysid ? rb_str_new2(sysid) : Qnil;
112
+ rb_funcall(handler, cbidOnExternalSubset, 3, rname, rextid, rsysid);
113
+ }
114
+ }
115
+
116
+ static void has_external_subset_callback(void *ctx)
117
+ {
118
+ VALUE handler = (VALUE) ctx;
119
+
120
+ if (handler != Qnil)
121
+ {
122
+ rb_funcall(handler, cbidOnHasExternalSubset, 0);
123
+ }
124
+ }
125
+
126
+ static void has_internal_subset_callback(void *ctx)
127
+ {
128
+ VALUE handler = (VALUE) ctx;
129
+
130
+ if (handler != Qnil)
131
+ {
132
+ rb_funcall(handler, cbidOnHasInternalSubset, 0);
133
+ }
134
+ }
135
+
136
+ static void internal_subset_callback(void *ctx, const char *name, const char *extid, const char *sysid)
137
+ {
138
+ VALUE handler = (VALUE) ctx;
139
+
140
+ if (handler != Qnil)
141
+ {
142
+ VALUE rname = name ? rb_str_new2(name) : Qnil;
143
+ VALUE rextid = extid ? rb_str_new2(extid) : Qnil;
144
+ VALUE rsysid = sysid ? rb_str_new2(sysid) : Qnil;
145
+ rb_funcall(handler, cbidOnInternalSubset, 3, rname, rextid, rsysid);
146
+ }
147
+ }
148
+
149
+ static void is_standalone_callback(void *ctx)
150
+ {
151
+ VALUE handler = (VALUE) ctx;
152
+
153
+ if (handler != Qnil)
154
+ {
155
+ rb_funcall(handler, cbidOnIsStandalone,0);
156
+ }
157
+ }
158
+
159
+ static void processing_instruction_callback(void *ctx, const char *target, const char *data)
160
+ {
161
+ VALUE handler = (VALUE) ctx;
162
+
163
+ if (handler != Qnil)
164
+ {
165
+ VALUE rtarget = target ? rb_str_new2(target) : Qnil;
166
+ VALUE rdata = data ? rb_str_new2(data) : Qnil;
167
+ rb_funcall(handler, cbidOnProcessingInstruction, 2, rtarget, rdata);
168
+ }
169
+ }
170
+
171
+ static void reference_callback(void *ctx, const char *name)
172
+ {
173
+ VALUE handler = (VALUE) ctx;
174
+
175
+ if (handler != Qnil)
176
+ {
177
+ rb_funcall(handler, cbidOnReference,1,rb_str_new2(name));
178
+ }
179
+ }
180
+
181
+ static void start_document_callback(void *ctx)
182
+ {
183
+ VALUE handler = (VALUE) ctx;
184
+
185
+ if (handler != Qnil)
186
+ {
187
+ rb_funcall(handler, cbidOnStartDocument, 0);
188
+ }
189
+ }
190
+
191
+ static void start_element_ns_callback(void *ctx,
192
+ const xmlChar *xlocalname, const xmlChar *xprefix, const xmlChar *xURI,
193
+ int nb_namespaces, const xmlChar **xnamespaces,
194
+ int nb_attributes, int nb_defaulted, const xmlChar **xattributes)
195
+ {
196
+ VALUE handler = (VALUE) ctx;
197
+ VALUE attributes = rb_hash_new();
198
+ VALUE namespaces = rb_hash_new();
199
+
200
+ if (handler == Qnil)
201
+ return;
202
+
203
+ if (xattributes)
204
+ {
205
+ /* Each attribute is an array of [localname, prefix, URI, value, end] */
206
+ int i;
207
+ for (i = 0;i < nb_attributes * 5; i+=5)
208
+ {
209
+ VALUE attrName = rb_str_new2(xattributes[i+0]);
210
+ VALUE attrValue = rb_str_new(xattributes[i+3], xattributes[i+4] - xattributes[i+3]);
211
+ /* VALUE attrPrefix = xattributes[i+1] ? rb_str_new2(xattributes[i+1]) : Qnil;
212
+ VALUE attrURI = xattributes[i+2] ? rb_str_new2(xattributes[i+2]) : Qnil; */
213
+
214
+ rb_hash_aset(attributes, attrName, attrValue);
215
+ }
216
+ }
217
+
218
+ if (xnamespaces)
219
+ {
220
+ int i;
221
+ for (i = 0;i < nb_namespaces * 2; i+=2)
222
+ {
223
+ VALUE nsPrefix = xnamespaces[i+0] ? rb_str_new2(xnamespaces[i+0]) : Qnil;
224
+ VALUE nsURI = xnamespaces[i+1] ? rb_str_new2(xnamespaces[i+1]) : Qnil;
225
+ rb_hash_aset(attributes, nsPrefix, nsURI);
226
+ }
227
+ }
228
+
229
+ /* Call start element for old-times sake */
230
+ if (rb_respond_to(handler, cbidOnStartElement))
231
+ {
232
+ VALUE name;
233
+ if (xprefix)
234
+ {
235
+ name = rb_str_new2(xprefix);
236
+ rb_str_cat2(name, ":");
237
+ rb_str_cat2(name, xlocalname);
238
+ }
239
+ else
240
+ {
241
+ name = rb_str_new2(xlocalname);
242
+ }
243
+ rb_funcall(handler, cbidOnStartElement, 2, name, attributes);
244
+ }
245
+
246
+ rb_funcall(handler, cbidOnStartElementNs, 5,
247
+ rb_str_new2(xlocalname),
248
+ attributes,
249
+ xprefix ? rb_str_new2(xprefix) : Qnil,
250
+ xURI ? rb_str_new2(xURI) : Qnil,
251
+ namespaces);
252
+ }
253
+
254
+ static void structured_error_callback(void *ctx, xmlErrorPtr xerror)
255
+ {
256
+ VALUE handler = (VALUE) ctx;
257
+
258
+ if (handler != Qnil)
259
+ {
260
+ VALUE error = rxml_error_wrap(xerror);
261
+ rb_funcall(handler, cbidOnError, 1, error);
262
+ }
263
+ }
264
+
265
+ /* ====== Handler =========== */
266
+ xmlSAXHandler rxml_sax_handler = {
267
+ (internalSubsetSAXFunc) internal_subset_callback,
268
+ (isStandaloneSAXFunc) is_standalone_callback,
269
+ (hasInternalSubsetSAXFunc) has_internal_subset_callback,
270
+ (hasExternalSubsetSAXFunc) has_external_subset_callback,
271
+ 0, /* resolveEntity */
272
+ 0, /* getEntity */
273
+ 0, /* entityDecl */
274
+ 0, /* notationDecl */
275
+ 0, /* attributeDecl */
276
+ 0, /* elementDecl */
277
+ 0, /* unparsedEntityDecl */
278
+ 0, /* setDocumentLocator */
279
+ (startDocumentSAXFunc) start_document_callback,
280
+ (endDocumentSAXFunc) end_document_callback,
281
+ 0, /* Use start_element_ns_callback instead */
282
+ 0, /* Use end_element_ns_callback instead */
283
+ (referenceSAXFunc) reference_callback,
284
+ (charactersSAXFunc) characters_callback,
285
+ 0, /* ignorableWhitespace */
286
+ (processingInstructionSAXFunc) processing_instruction_callback,
287
+ (commentSAXFunc) comment_callback,
288
+ 0, /* xmlStructuredErrorFunc is used instead */
289
+ 0, /* xmlStructuredErrorFunc is used instead */
290
+ 0, /* xmlStructuredErrorFunc is used instead */
291
+ 0, /* xmlGetParameterEntity */
292
+ (cdataBlockSAXFunc) cdata_block_callback,
293
+ (externalSubsetSAXFunc) external_subset_callback,
294
+ XML_SAX2_MAGIC, /* force SAX2 */
295
+ 0, /* _private */
296
+ (startElementNsSAX2Func) start_element_ns_callback,
297
+ (endElementNsSAX2Func) end_element_ns_callback,
298
+ (xmlStructuredErrorFunc) structured_error_callback
299
+ };
300
+
301
+ void ruby_init_xml_sax2_handler(void)
302
+ {
303
+
304
+ /* SaxCallbacks */
305
+ cbidOnCdataBlock = rb_intern("on_cdata_block");
306
+ cbidOnCharacters = rb_intern("on_characters");
307
+ cbidOnComment = rb_intern("on_comment");
308
+ cbidOnEndDocument = rb_intern("on_end_document");
309
+ cbidOnEndElement = rb_intern("on_end_element");
310
+ cbidOnEndElementNs = rb_intern("on_end_element_ns");
311
+ cbidOnError = rb_intern("on_error");
312
+ cbidOnExternalSubset = rb_intern("on_external_subset");
313
+ cbidOnHasExternalSubset = rb_intern("on_has_external_subset");
314
+ cbidOnHasInternalSubset = rb_intern("on_has_internal_subset");
315
+ cbidOnInternalSubset = rb_intern("on_internal_subset");
316
+ cbidOnIsStandalone = rb_intern("on_is_standalone");
317
+ cbidOnProcessingInstruction = rb_intern("on_processing_instruction");
318
+ cbidOnReference = rb_intern("on_reference");
319
+ cbidOnStartElement = rb_intern("on_start_element");
320
+ cbidOnStartElementNs = rb_intern("on_start_element_ns");
321
+ cbidOnStartDocument = rb_intern("on_start_document");
322
+ }
@@ -0,0 +1,12 @@
1
+ /* $Id: ruby_xml_sax_parser.h 666 2008-12-07 00:16:50Z cfis $ */
2
+
3
+ /* Please see the LICENSE file for copyright and distribution information */
4
+
5
+ #ifndef __RXML_SAX2_HANDLER__
6
+ #define __RXML_SAX2_HANDLER__
7
+
8
+ extern xmlSAXHandler rxml_sax_handler;
9
+
10
+ void ruby_init_xml_sax2_handler(void);
11
+
12
+ #endif
@@ -1,4 +1,4 @@
1
- /* $Id: ruby_xml_sax_parser.c 650 2008-11-30 03:40:22Z cfis $ */
1
+ /* $Id: ruby_xml_sax_parser.c 684 2008-12-13 00:34:28Z cfis $ */
2
2
 
3
3
  /* Please see the LICENSE file for copyright and distribution information */
4
4
 
@@ -36,31 +36,12 @@
36
36
  */
37
37
 
38
38
  VALUE cXMLSaxParser;
39
- VALUE mXMLSaxParserCallbacks;
40
39
 
41
40
  static ID INPUT_ATTR;
42
41
  static ID CALLBACKS_ATTR;
43
42
 
44
- VALUE cbidOnInternalSubset;
45
- VALUE cbidOnIsStandalone;
46
- VALUE cbidOnHasInternalSubset;
47
- VALUE cbidOnHasExternalSubset;
48
- VALUE cbidOnStartDocument;
49
- VALUE cbidOnEndDocument;
50
- VALUE cbidOnStartElement;
51
- VALUE cbidOnEndElement;
52
- VALUE cbidOnReference;
53
- VALUE cbidOnCharacters;
54
- VALUE cbidOnProcessingInstruction;
55
- VALUE cbidOnComment;
56
- VALUE cbidOnXmlParserWarning;
57
- VALUE cbidOnXmlParserError;
58
- VALUE cbidOnXmlParserFatalError;
59
- VALUE cbidOnCdataBlock;
60
- VALUE cbidOnExternalSubset;
61
-
62
- #include "sax_parser_callbacks.inc"
63
43
 
44
+ /* ====== Parser =========== */
64
45
  /*
65
46
  * call-seq:
66
47
  * sax_parser.initialize -> sax_parser
@@ -77,26 +58,29 @@ static VALUE rxml_sax_parser_initialize(VALUE self)
77
58
  /* Parsing data sources */
78
59
  static int rxml_sax_parser_parse_file(VALUE self, VALUE input)
79
60
  {
61
+ VALUE handler = rb_ivar_get(self, CALLBACKS_ATTR);
80
62
  VALUE file = rb_ivar_get(input, FILE_ATTR);
81
- return xmlSAXUserParseFile((xmlSAXHandlerPtr) & rxml_sax_hander_struct,
82
- (void *) self, StringValuePtr(file));
63
+ return xmlSAXUserParseFile((xmlSAXHandlerPtr) &rxml_sax_handler,
64
+ (void *) handler, StringValuePtr(file));
83
65
  }
84
66
 
85
67
  static int rxml_sax_parser_parse_string(VALUE self, VALUE input)
86
68
  {
69
+ VALUE handler = rb_ivar_get(self, CALLBACKS_ATTR);
87
70
  VALUE str = rb_ivar_get(input, STRING_ATTR);
88
- return xmlSAXUserParseMemory((xmlSAXHandlerPtr) & rxml_sax_hander_struct,
89
- (void *) self, StringValuePtr(str), RSTRING_LEN(str));
71
+ return xmlSAXUserParseMemory((xmlSAXHandlerPtr) &rxml_sax_handler,
72
+ (void *) handler, StringValuePtr(str), RSTRING_LEN(str));
90
73
  }
91
74
 
92
75
  static int rxml_sax_parser_parse_io(VALUE self, VALUE input)
93
76
  {
77
+ VALUE handler = rb_ivar_get(self, CALLBACKS_ATTR);
94
78
  VALUE io = rb_ivar_get(input, IO_ATTR);
95
79
  VALUE encoding = rb_ivar_get(input, ENCODING_ATTR);
96
80
  xmlCharEncoding xmlEncoding = NUM2INT(encoding);
97
81
  xmlParserCtxtPtr ctxt =
98
- xmlCreateIOParserCtxt((xmlSAXHandlerPtr) & rxml_sax_hander_struct,
99
- (void *) self, (xmlInputReadCallback) rxml_read_callback, NULL,
82
+ xmlCreateIOParserCtxt((xmlSAXHandlerPtr) &rxml_sax_handler,
83
+ (void *) handler, (xmlInputReadCallback) rxml_read_callback, NULL,
100
84
  (void *) io, xmlEncoding);
101
85
  return xmlParseDocument(ctxt);
102
86
  }
@@ -153,23 +137,4 @@ void ruby_init_xml_sax_parser(void)
153
137
  /* Instance Methods */
154
138
  rb_define_method(cXMLSaxParser, "initialize", rxml_sax_parser_initialize, 0);
155
139
  rb_define_method(cXMLSaxParser, "parse", rxml_sax_parser_parse, 0);
156
-
157
- /* SaxCallbacks */
158
- cbidOnInternalSubset = rb_intern("on_internal_subset");
159
- cbidOnIsStandalone = rb_intern("on_is_standalone");
160
- cbidOnHasInternalSubset = rb_intern("on_has_internal_subset");
161
- cbidOnHasExternalSubset = rb_intern("on_has_external_subset");
162
- cbidOnStartDocument = rb_intern("on_start_document");
163
- cbidOnEndDocument = rb_intern("on_end_document");
164
- cbidOnStartElement = rb_intern("on_start_element");
165
- cbidOnEndElement = rb_intern("on_end_element");
166
- cbidOnReference = rb_intern("on_reference");
167
- cbidOnCharacters = rb_intern("on_characters");
168
- cbidOnProcessingInstruction = rb_intern("on_processing_instruction");
169
- cbidOnComment = rb_intern("on_comment");
170
- cbidOnXmlParserWarning = rb_intern("on_parser_warning");
171
- cbidOnXmlParserError = rb_intern("on_parser_error");
172
- cbidOnXmlParserFatalError = rb_intern("on_parser_fatal_error");
173
- cbidOnCdataBlock = rb_intern("on_cdata_block");
174
- cbidOnExternalSubset = rb_intern("on_external_subset");
175
- }
140
+ }
@@ -1,354 +1,354 @@
1
- /* $Id: ruby_xml_xpath_context.c 673 2008-12-08 06:33:23Z 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
- #include <st.h>
9
-
10
- /*
11
- * Document-class: LibXML::XML::XPath::Context
12
- *
13
- * The XML::XPath::Context class is used to evaluate XPath
14
- * expressions. Generally, you should not directly use this class,
15
- * but instead use the XML::Document#find and XML::Node#find methods.
16
- *
17
- * doc = XML::Document.string('<header>content</header>')
18
- * context = XPath::Context.new(doc)
19
- * context.node = doc.root
20
- * context.register_namespaces_from_node(doc.root)
21
- * nodes = context.find('/header')
22
- */
23
-
24
- VALUE cXMLXPathContext;
25
-
26
- static void rxml_xpath_context_free(xmlXPathContextPtr ctxt)
27
- {
28
- xmlXPathFreeContext(ctxt);
29
- }
30
-
31
- static VALUE rxml_xpath_context_alloc(VALUE klass)
32
- {
33
- return Data_Wrap_Struct(cXMLXPathContext, NULL, rxml_xpath_context_free, NULL);
34
- }
35
-
36
- /* call-seq:
37
- * XPath::Context.new(node) -> XPath::Context
38
- *
39
- * Creates a new XPath context for the specified document. The
40
- * context can then be used to evaluate an XPath expression.
41
- *
42
- * doc = XML::Document.string('<header><first>hi</first></header>')
43
- * context = XPath::Context.new(doc)
44
- * nodes = XPath::Object.new('//first', context)
45
- * nodes.length == 1
46
- */
47
- static VALUE rxml_xpath_context_initialize(VALUE self, VALUE node)
48
- {
49
- xmlDocPtr xdoc;
50
- VALUE document;
51
-
52
- if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
53
- {
54
- document = rb_funcall(node, rb_intern("doc"), 0);
55
- if (NIL_P(document))
56
- rb_raise(rb_eTypeError, "Supplied node must belong to a document.");
57
- }
58
- else if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
59
- {
60
- document = node;
61
- }
62
- else
63
- {
64
- rb_raise(rb_eTypeError, "Supplied argument must be a document or node.");
65
- }
66
-
67
- Data_Get_Struct(document, xmlDoc, xdoc);
68
- DATA_PTR(self) = xmlXPathNewContext(xdoc);
69
-
70
- /* Save the doc as an attribute, this will expose it to Ruby's GC. */
71
- rb_iv_set(self, "@doc", document);
72
-
73
- return self;
74
- }
75
-
76
- /*
77
- * call-seq:
78
- * context.register_namespace(prefix, uri) -> (true|false)
79
- *
80
- * Register the specified namespace URI with the specified prefix
81
- * in this context.
82
-
83
- * context.register_namespace('xi', 'http://www.w3.org/2001/XInclude')
84
- */
85
- static VALUE rxml_xpath_context_register_namespace(VALUE self, VALUE prefix, VALUE uri)
86
- {
87
- xmlXPathContextPtr ctxt;
88
- Data_Get_Struct(self, xmlXPathContext, ctxt);
89
-
90
- /* Prefix could be a symbol. */
91
- prefix = rb_obj_as_string(prefix);
92
-
93
- if (xmlXPathRegisterNs(ctxt, (xmlChar*) StringValuePtr(prefix),
94
- (xmlChar*) StringValuePtr(uri)) == 0)
95
- {
96
- return (Qtrue);
97
- }
98
- else
99
- {
100
- /* Should raise an exception, IMHO (whose?, why shouldnt it? -danj)*/
101
- rb_warning("register namespace failed");
102
- return (Qfalse);
103
- }
104
- }
105
-
106
- /* call-seq:
107
- * context.register_namespaces_from_node(node) -> self
108
- *
109
- * Helper method to read in namespaces defined on a node.
110
- *
111
- * doc = XML::Document.string('<header><first>hi</first></header>')
112
- * context = XPath::Context.new(doc)
113
- * context.register_namespaces_from_node(doc.root)
114
- */
115
- static VALUE rxml_xpath_context_register_namespaces_from_node(VALUE self,
116
- VALUE node)
117
- {
118
- xmlXPathContextPtr xctxt;
119
- xmlNodePtr xnode;
120
- xmlNsPtr *xnsArr;
121
-
122
- Data_Get_Struct(self, xmlXPathContext, xctxt);
123
-
124
- if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
125
- {
126
- xmlDocPtr xdoc;
127
- Data_Get_Struct(node, xmlDoc, xdoc);
128
- xnode = xmlDocGetRootElement(xdoc);
129
- }
130
- else if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
131
- {
132
- Data_Get_Struct(node, xmlNode, xnode);
133
- }
134
- else
135
- {
136
- rb_raise(rb_eTypeError, "The first argument must be a document or node.");
137
- }
138
-
139
- xnsArr = xmlGetNsList(xnode->doc, xnode);
140
-
141
- if (xnsArr)
142
- {
143
- xmlNsPtr xns = *xnsArr;
144
-
145
- while (xns)
146
- {
147
- /* If there is no prefix, then this is the default namespace.
148
- Skip it for now. */
149
- if (xns->prefix)
150
- {
151
- VALUE prefix = rb_str_new2((const char*)xns->prefix);
152
- VALUE uri = rb_str_new2((const char*)xns->href);
153
- rxml_xpath_context_register_namespace(self, prefix, uri);
154
- }
155
- xns = xns->next;
156
- }
157
- xmlFree(xnsArr);
158
- }
159
-
160
- return self;
161
- }
162
-
163
- static int iterate_ns_hash(st_data_t prefix, st_data_t uri, st_data_t self)
164
- {
165
- rxml_xpath_context_register_namespace(self, prefix, uri);
166
- return ST_CONTINUE;
167
- }
168
-
169
- /*
170
- * call-seq:
171
- * context.register_namespaces(["prefix:uri"]) -> self
172
- *
173
- * Register the specified namespaces in this context. There are
174
- * three different forms that libxml accepts. These include
175
- * a string, an array of strings, or a hash table:
176
- *
177
- * context.register_namespaces('xi:http://www.w3.org/2001/XInclude')
178
- * context.register_namespaces(['xlink:http://www.w3.org/1999/xlink',
179
- * 'xi:http://www.w3.org/2001/XInclude')
180
- * context.register_namespaces('xlink' => 'http://www.w3.org/1999/xlink',
181
- * 'xi' => 'http://www.w3.org/2001/XInclude')
182
- */
183
- static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
184
- {
185
- char *cp;
186
- long i;
187
- VALUE rprefix, ruri;
188
-
189
- /* Need to loop through the 2nd argument and iterate through the
190
- * list of namespaces that we want to allow */
191
- switch (TYPE(nslist))
192
- {
193
- case T_STRING:
194
- cp = strchr(StringValuePtr(nslist), (int) ':');
195
- if (cp == NULL)
196
- {
197
- rprefix = nslist;
198
- ruri = Qnil;
199
- }
200
- else
201
- {
202
- rprefix = rb_str_new(StringValuePtr(nslist), (int) ((long) cp
203
- - (long) StringValuePtr(nslist)));
204
- ruri = rb_str_new2(&cp[1]);
205
- }
206
- /* Should test the results of this */
207
- rxml_xpath_context_register_namespace(self, rprefix, ruri);
208
- break;
209
- case T_ARRAY:
210
- for (i = 0; i < RARRAY_LEN(nslist); i++)
211
- {
212
- rxml_xpath_context_register_namespaces(self, RARRAY_PTR(nslist)[i]);
213
- }
214
- break;
215
- case T_HASH:
216
- st_foreach(RHASH_TBL(nslist), iterate_ns_hash, self);
217
- break;
218
- default:
219
- rb_raise(
220
- rb_eArgError,
221
- "Invalid argument type, only accept string, array of strings, or an array of arrays");
222
- }
223
- return self;
224
- }
225
-
226
- /*
227
- * call-seq:
228
- * context.node = node
229
- *
230
- * Set the current node used by the XPath engine
231
-
232
- * doc = XML::Document.string('<header><first>hi</first></header>')
233
- * context.node = doc.root.first
234
- */
235
- static VALUE rxml_xpath_context_node_set(VALUE self, VALUE node)
236
- {
237
- xmlXPathContextPtr xctxt;
238
- xmlNodePtr xnode;
239
-
240
- Data_Get_Struct(self, xmlXPathContext, xctxt);
241
- Data_Get_Struct(node, xmlNode, xnode);
242
- xctxt->node = xnode;
243
- return node;
244
- }
245
-
246
- /*
247
- * call-seq:
248
- * context.find("xpath") -> XML::XPath::Object
249
- *
250
- * Find nodes matching the specified XPath expression
251
- */
252
- static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
253
- {
254
- xmlXPathContextPtr xctxt;
255
- xmlXPathObjectPtr xobject;
256
- xmlXPathCompExprPtr xcompexpr;
257
- VALUE result;
258
-
259
- Data_Get_Struct(self, xmlXPathContext, xctxt);
260
-
261
- if (TYPE(xpath_expr) == T_STRING)
262
- {
263
- VALUE expression = rb_check_string_type(xpath_expr);
264
- xobject = xmlXPathEval((xmlChar*) StringValueCStr(expression), xctxt);
265
- }
266
- else if (rb_obj_is_kind_of(xpath_expr, cXMLXPathExpression))
267
- {
268
- Data_Get_Struct(xpath_expr, xmlXPathCompExpr, xcompexpr);
269
- xobject = xmlXPathCompiledEval(xcompexpr, xctxt);
270
- }
271
- else
272
- {
273
- rb_raise(rb_eTypeError,
274
- "Argument should be an intance of a String or XPath::Expression");
275
- }
276
-
277
- if (xobject == NULL)
278
- {
279
- /* xmlLastError is different than xctxt->lastError. Use
280
- xmlLastError since it has the message set while xctxt->lastError
281
- does not. */
282
- xmlErrorPtr xerror = xmlGetLastError();
283
- rxml_raise(xerror);
284
- }
285
-
286
- result = rxml_xpath_object_wrap(xobject);
287
- rb_iv_set(result, "@context", self);
288
- return result;
289
- }
290
-
291
-
292
- /*
293
- * call-seq:
294
- * context.enable_cache(size = nil)
295
- *
296
- * Enables an XPath::Context's built-in cache. If the cache is
297
- * enabled then XPath objects will be cached internally for reuse.
298
- * The size parameter controls sets the maximum number of XPath objects
299
- * that will be cached per XPath object type (node-set, string, number,
300
- * boolean, and misc objects). Set size to nil to use the default
301
- * cache size of 100.
302
- */
303
- static VALUE
304
- rxml_xpath_context_enable_cache(int argc, VALUE *argv, VALUE self)
305
- {
306
- xmlXPathContextPtr xctxt;
307
- VALUE size;
308
- int value = -1;
309
-
310
- Data_Get_Struct(self, xmlXPathContext, xctxt);
311
-
312
- if (rb_scan_args(argc, argv, "01", &size) == 1)
313
- {
314
- value = NUM2INT(size);
315
- }
316
-
317
- if (xmlXPathContextSetCache(xctxt, 1, value, 0) == -1)
318
- rxml_raise(&xmlLastError);
319
-
320
- return self;
321
- }
322
-
323
- /*
324
- * call-seq:
325
- * context.disable_cache
326
- *
327
- * Disables an XPath::Context's built-in cache.
328
- */
329
- static VALUE
330
- rxml_xpath_context_disable_cache(VALUE self) {
331
- xmlXPathContextPtr xctxt;
332
- Data_Get_Struct(self, xmlXPathContext, xctxt);
333
-
334
- if (xmlXPathContextSetCache(xctxt, 0, 0, 0) == -1)
335
- rxml_raise(&xmlLastError);
336
-
337
- return self;
338
- }
339
-
340
-
341
- void ruby_init_xml_xpath_context(void)
342
- {
343
- cXMLXPathContext = rb_define_class_under(mXPath, "Context", rb_cObject);
344
- rb_define_alloc_func(cXMLXPathContext, rxml_xpath_context_alloc);
345
- rb_define_attr(cXMLXPathContext, "doc", 1, 0);
346
- rb_define_method(cXMLXPathContext, "initialize", rxml_xpath_context_initialize, 1);
347
- rb_define_method(cXMLXPathContext, "register_namespaces", rxml_xpath_context_register_namespaces, 1);
348
- rb_define_method(cXMLXPathContext, "register_namespaces_from_node", rxml_xpath_context_register_namespaces_from_node, 1);
349
- rb_define_method(cXMLXPathContext, "register_namespace", rxml_xpath_context_register_namespace, 2);
350
- rb_define_method(cXMLXPathContext, "node=", rxml_xpath_context_node_set, 1);
351
- rb_define_method(cXMLXPathContext, "find", rxml_xpath_context_find, 1);
352
- rb_define_method(cXMLXPathContext, "enable_cache", rxml_xpath_context_enable_cache, -1);
353
- rb_define_method(cXMLXPathContext, "disable_cache", rxml_xpath_context_disable_cache, 0);
354
- }
1
+ /* $Id: ruby_xml_xpath_context.c 673 2008-12-08 06:33:23Z 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
+ #include <st.h>
9
+
10
+ /*
11
+ * Document-class: LibXML::XML::XPath::Context
12
+ *
13
+ * The XML::XPath::Context class is used to evaluate XPath
14
+ * expressions. Generally, you should not directly use this class,
15
+ * but instead use the XML::Document#find and XML::Node#find methods.
16
+ *
17
+ * doc = XML::Document.string('<header>content</header>')
18
+ * context = XPath::Context.new(doc)
19
+ * context.node = doc.root
20
+ * context.register_namespaces_from_node(doc.root)
21
+ * nodes = context.find('/header')
22
+ */
23
+
24
+ VALUE cXMLXPathContext;
25
+
26
+ static void rxml_xpath_context_free(xmlXPathContextPtr ctxt)
27
+ {
28
+ xmlXPathFreeContext(ctxt);
29
+ }
30
+
31
+ static VALUE rxml_xpath_context_alloc(VALUE klass)
32
+ {
33
+ return Data_Wrap_Struct(cXMLXPathContext, NULL, rxml_xpath_context_free, NULL);
34
+ }
35
+
36
+ /* call-seq:
37
+ * XPath::Context.new(node) -> XPath::Context
38
+ *
39
+ * Creates a new XPath context for the specified document. The
40
+ * context can then be used to evaluate an XPath expression.
41
+ *
42
+ * doc = XML::Document.string('<header><first>hi</first></header>')
43
+ * context = XPath::Context.new(doc)
44
+ * nodes = XPath::Object.new('//first', context)
45
+ * nodes.length == 1
46
+ */
47
+ static VALUE rxml_xpath_context_initialize(VALUE self, VALUE node)
48
+ {
49
+ xmlDocPtr xdoc;
50
+ VALUE document;
51
+
52
+ if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
53
+ {
54
+ document = rb_funcall(node, rb_intern("doc"), 0);
55
+ if (NIL_P(document))
56
+ rb_raise(rb_eTypeError, "Supplied node must belong to a document.");
57
+ }
58
+ else if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
59
+ {
60
+ document = node;
61
+ }
62
+ else
63
+ {
64
+ rb_raise(rb_eTypeError, "Supplied argument must be a document or node.");
65
+ }
66
+
67
+ Data_Get_Struct(document, xmlDoc, xdoc);
68
+ DATA_PTR(self) = xmlXPathNewContext(xdoc);
69
+
70
+ /* Save the doc as an attribute, this will expose it to Ruby's GC. */
71
+ rb_iv_set(self, "@doc", document);
72
+
73
+ return self;
74
+ }
75
+
76
+ /*
77
+ * call-seq:
78
+ * context.register_namespace(prefix, uri) -> (true|false)
79
+ *
80
+ * Register the specified namespace URI with the specified prefix
81
+ * in this context.
82
+
83
+ * context.register_namespace('xi', 'http://www.w3.org/2001/XInclude')
84
+ */
85
+ static VALUE rxml_xpath_context_register_namespace(VALUE self, VALUE prefix, VALUE uri)
86
+ {
87
+ xmlXPathContextPtr ctxt;
88
+ Data_Get_Struct(self, xmlXPathContext, ctxt);
89
+
90
+ /* Prefix could be a symbol. */
91
+ prefix = rb_obj_as_string(prefix);
92
+
93
+ if (xmlXPathRegisterNs(ctxt, (xmlChar*) StringValuePtr(prefix),
94
+ (xmlChar*) StringValuePtr(uri)) == 0)
95
+ {
96
+ return (Qtrue);
97
+ }
98
+ else
99
+ {
100
+ /* Should raise an exception, IMHO (whose?, why shouldnt it? -danj)*/
101
+ rb_warning("register namespace failed");
102
+ return (Qfalse);
103
+ }
104
+ }
105
+
106
+ /* call-seq:
107
+ * context.register_namespaces_from_node(node) -> self
108
+ *
109
+ * Helper method to read in namespaces defined on a node.
110
+ *
111
+ * doc = XML::Document.string('<header><first>hi</first></header>')
112
+ * context = XPath::Context.new(doc)
113
+ * context.register_namespaces_from_node(doc.root)
114
+ */
115
+ static VALUE rxml_xpath_context_register_namespaces_from_node(VALUE self,
116
+ VALUE node)
117
+ {
118
+ xmlXPathContextPtr xctxt;
119
+ xmlNodePtr xnode;
120
+ xmlNsPtr *xnsArr;
121
+
122
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
123
+
124
+ if (rb_obj_is_kind_of(node, cXMLDocument) == Qtrue)
125
+ {
126
+ xmlDocPtr xdoc;
127
+ Data_Get_Struct(node, xmlDoc, xdoc);
128
+ xnode = xmlDocGetRootElement(xdoc);
129
+ }
130
+ else if (rb_obj_is_kind_of(node, cXMLNode) == Qtrue)
131
+ {
132
+ Data_Get_Struct(node, xmlNode, xnode);
133
+ }
134
+ else
135
+ {
136
+ rb_raise(rb_eTypeError, "The first argument must be a document or node.");
137
+ }
138
+
139
+ xnsArr = xmlGetNsList(xnode->doc, xnode);
140
+
141
+ if (xnsArr)
142
+ {
143
+ xmlNsPtr xns = *xnsArr;
144
+
145
+ while (xns)
146
+ {
147
+ /* If there is no prefix, then this is the default namespace.
148
+ Skip it for now. */
149
+ if (xns->prefix)
150
+ {
151
+ VALUE prefix = rb_str_new2((const char*)xns->prefix);
152
+ VALUE uri = rb_str_new2((const char*)xns->href);
153
+ rxml_xpath_context_register_namespace(self, prefix, uri);
154
+ }
155
+ xns = xns->next;
156
+ }
157
+ xmlFree(xnsArr);
158
+ }
159
+
160
+ return self;
161
+ }
162
+
163
+ static int iterate_ns_hash(st_data_t prefix, st_data_t uri, st_data_t self)
164
+ {
165
+ rxml_xpath_context_register_namespace(self, prefix, uri);
166
+ return ST_CONTINUE;
167
+ }
168
+
169
+ /*
170
+ * call-seq:
171
+ * context.register_namespaces(["prefix:uri"]) -> self
172
+ *
173
+ * Register the specified namespaces in this context. There are
174
+ * three different forms that libxml accepts. These include
175
+ * a string, an array of strings, or a hash table:
176
+ *
177
+ * context.register_namespaces('xi:http://www.w3.org/2001/XInclude')
178
+ * context.register_namespaces(['xlink:http://www.w3.org/1999/xlink',
179
+ * 'xi:http://www.w3.org/2001/XInclude')
180
+ * context.register_namespaces('xlink' => 'http://www.w3.org/1999/xlink',
181
+ * 'xi' => 'http://www.w3.org/2001/XInclude')
182
+ */
183
+ static VALUE rxml_xpath_context_register_namespaces(VALUE self, VALUE nslist)
184
+ {
185
+ char *cp;
186
+ long i;
187
+ VALUE rprefix, ruri;
188
+
189
+ /* Need to loop through the 2nd argument and iterate through the
190
+ * list of namespaces that we want to allow */
191
+ switch (TYPE(nslist))
192
+ {
193
+ case T_STRING:
194
+ cp = strchr(StringValuePtr(nslist), (int) ':');
195
+ if (cp == NULL)
196
+ {
197
+ rprefix = nslist;
198
+ ruri = Qnil;
199
+ }
200
+ else
201
+ {
202
+ rprefix = rb_str_new(StringValuePtr(nslist), (int) ((long) cp
203
+ - (long) StringValuePtr(nslist)));
204
+ ruri = rb_str_new2(&cp[1]);
205
+ }
206
+ /* Should test the results of this */
207
+ rxml_xpath_context_register_namespace(self, rprefix, ruri);
208
+ break;
209
+ case T_ARRAY:
210
+ for (i = 0; i < RARRAY_LEN(nslist); i++)
211
+ {
212
+ rxml_xpath_context_register_namespaces(self, RARRAY_PTR(nslist)[i]);
213
+ }
214
+ break;
215
+ case T_HASH:
216
+ st_foreach(RHASH_TBL(nslist), iterate_ns_hash, self);
217
+ break;
218
+ default:
219
+ rb_raise(
220
+ rb_eArgError,
221
+ "Invalid argument type, only accept string, array of strings, or an array of arrays");
222
+ }
223
+ return self;
224
+ }
225
+
226
+ /*
227
+ * call-seq:
228
+ * context.node = node
229
+ *
230
+ * Set the current node used by the XPath engine
231
+
232
+ * doc = XML::Document.string('<header><first>hi</first></header>')
233
+ * context.node = doc.root.first
234
+ */
235
+ static VALUE rxml_xpath_context_node_set(VALUE self, VALUE node)
236
+ {
237
+ xmlXPathContextPtr xctxt;
238
+ xmlNodePtr xnode;
239
+
240
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
241
+ Data_Get_Struct(node, xmlNode, xnode);
242
+ xctxt->node = xnode;
243
+ return node;
244
+ }
245
+
246
+ /*
247
+ * call-seq:
248
+ * context.find("xpath") -> XML::XPath::Object
249
+ *
250
+ * Find nodes matching the specified XPath expression
251
+ */
252
+ static VALUE rxml_xpath_context_find(VALUE self, VALUE xpath_expr)
253
+ {
254
+ xmlXPathContextPtr xctxt;
255
+ xmlXPathObjectPtr xobject;
256
+ xmlXPathCompExprPtr xcompexpr;
257
+ VALUE result;
258
+
259
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
260
+
261
+ if (TYPE(xpath_expr) == T_STRING)
262
+ {
263
+ VALUE expression = rb_check_string_type(xpath_expr);
264
+ xobject = xmlXPathEval((xmlChar*) StringValueCStr(expression), xctxt);
265
+ }
266
+ else if (rb_obj_is_kind_of(xpath_expr, cXMLXPathExpression))
267
+ {
268
+ Data_Get_Struct(xpath_expr, xmlXPathCompExpr, xcompexpr);
269
+ xobject = xmlXPathCompiledEval(xcompexpr, xctxt);
270
+ }
271
+ else
272
+ {
273
+ rb_raise(rb_eTypeError,
274
+ "Argument should be an intance of a String or XPath::Expression");
275
+ }
276
+
277
+ if (xobject == NULL)
278
+ {
279
+ /* xmlLastError is different than xctxt->lastError. Use
280
+ xmlLastError since it has the message set while xctxt->lastError
281
+ does not. */
282
+ xmlErrorPtr xerror = xmlGetLastError();
283
+ rxml_raise(xerror);
284
+ }
285
+
286
+ result = rxml_xpath_object_wrap(xobject);
287
+ rb_iv_set(result, "@context", self);
288
+ return result;
289
+ }
290
+
291
+
292
+ /*
293
+ * call-seq:
294
+ * context.enable_cache(size = nil)
295
+ *
296
+ * Enables an XPath::Context's built-in cache. If the cache is
297
+ * enabled then XPath objects will be cached internally for reuse.
298
+ * The size parameter controls sets the maximum number of XPath objects
299
+ * that will be cached per XPath object type (node-set, string, number,
300
+ * boolean, and misc objects). Set size to nil to use the default
301
+ * cache size of 100.
302
+ */
303
+ static VALUE
304
+ rxml_xpath_context_enable_cache(int argc, VALUE *argv, VALUE self)
305
+ {
306
+ xmlXPathContextPtr xctxt;
307
+ VALUE size;
308
+ int value = -1;
309
+
310
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
311
+
312
+ if (rb_scan_args(argc, argv, "01", &size) == 1)
313
+ {
314
+ value = NUM2INT(size);
315
+ }
316
+
317
+ if (xmlXPathContextSetCache(xctxt, 1, value, 0) == -1)
318
+ rxml_raise(&xmlLastError);
319
+
320
+ return self;
321
+ }
322
+
323
+ /*
324
+ * call-seq:
325
+ * context.disable_cache
326
+ *
327
+ * Disables an XPath::Context's built-in cache.
328
+ */
329
+ static VALUE
330
+ rxml_xpath_context_disable_cache(VALUE self) {
331
+ xmlXPathContextPtr xctxt;
332
+ Data_Get_Struct(self, xmlXPathContext, xctxt);
333
+
334
+ if (xmlXPathContextSetCache(xctxt, 0, 0, 0) == -1)
335
+ rxml_raise(&xmlLastError);
336
+
337
+ return self;
338
+ }
339
+
340
+
341
+ void ruby_init_xml_xpath_context(void)
342
+ {
343
+ cXMLXPathContext = rb_define_class_under(mXPath, "Context", rb_cObject);
344
+ rb_define_alloc_func(cXMLXPathContext, rxml_xpath_context_alloc);
345
+ rb_define_attr(cXMLXPathContext, "doc", 1, 0);
346
+ rb_define_method(cXMLXPathContext, "initialize", rxml_xpath_context_initialize, 1);
347
+ rb_define_method(cXMLXPathContext, "register_namespaces", rxml_xpath_context_register_namespaces, 1);
348
+ rb_define_method(cXMLXPathContext, "register_namespaces_from_node", rxml_xpath_context_register_namespaces_from_node, 1);
349
+ rb_define_method(cXMLXPathContext, "register_namespace", rxml_xpath_context_register_namespace, 2);
350
+ rb_define_method(cXMLXPathContext, "node=", rxml_xpath_context_node_set, 1);
351
+ rb_define_method(cXMLXPathContext, "find", rxml_xpath_context_find, 1);
352
+ rb_define_method(cXMLXPathContext, "enable_cache", rxml_xpath_context_enable_cache, -1);
353
+ rb_define_method(cXMLXPathContext, "disable_cache", rxml_xpath_context_disable_cache, 0);
354
+ }