blackwinter-libxslt-ruby 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,235 @@
1
+ /* $Id: libxslt.c 42 2007-12-07 06:09:35Z transami $ */
2
+
3
+ /* Please see the LICENSE file for copyright and distribution information */
4
+
5
+ #include "libxslt.h"
6
+ #include "libxml/xmlversion.h"
7
+
8
+ VALUE cLibXSLT;
9
+ VALUE cXSLT;
10
+ VALUE eXSLTError;
11
+
12
+ /*
13
+ * Document-class: LibXSLT::XSLT
14
+ *
15
+ * The libxslt gem provides Ruby language bindings for GNOME's Libxslt
16
+ * toolkit. It is free software, released under the MIT License.
17
+ *
18
+ * Using the bindings is straightforward:
19
+ *
20
+ * stylesheet_doc = XML::Document.file('stylesheet_file')
21
+ * stylesheet = XSLT::Stylesheet.new(stylesheet_doc)
22
+ *
23
+ * xml_doc = XML::Document.file('xml_file')
24
+ * result = stylesheet.apply(xml_doc)
25
+ *
26
+ */
27
+
28
+ #ifdef RDOC_NEVER_DEFINED
29
+ cLibXSLT = rb_define_module("XSLT");
30
+ #endif
31
+
32
+
33
+ #if defined(_WIN32)
34
+ __declspec(dllexport)
35
+ #endif
36
+
37
+ VALUE
38
+ xpathObj2value(xmlXPathObjectPtr obj, xmlDocPtr doc) {
39
+ VALUE ret = Qnil;
40
+
41
+ if (obj == NULL) {
42
+ return ret;
43
+ }
44
+
45
+ switch (obj->type) {
46
+ case XPATH_NODESET:
47
+ ret = rb_ary_new();
48
+
49
+ if ((obj->nodesetval != NULL) && (obj->nodesetval->nodeNr != 0)) {
50
+ xmlBufferPtr buff = xmlBufferCreate();
51
+ xmlNodePtr node;
52
+ int i;
53
+
54
+ /* this assumes all the nodes are elements, which is a bad idea */
55
+ for (i = 0; i < obj->nodesetval->nodeNr; i++) {
56
+ node = obj->nodesetval->nodeTab[i];
57
+
58
+ if (node->type == XML_ELEMENT_NODE) {
59
+ xmlNodeDump(buff, doc, node, 0, 0);
60
+
61
+ rb_ary_push(ret, rb_funcall(rb_funcall(cXMLDocument, rb_intern("string"),
62
+ 1, rb_str_new2((char *)buff->content)), rb_intern("root"), 0));
63
+
64
+ /* empty the buffer (xmlNodeDump appends rather than replaces) */
65
+ xmlBufferEmpty(buff);
66
+ }
67
+ else if (node->type == XML_TEXT_NODE) {
68
+ rb_ary_push(ret, rb_str_new2((char *)node->content));
69
+ }
70
+ else if (node->type == XML_ATTRIBUTE_NODE) {
71
+ /* BUG: should ensure children is not null (shouldn't) */
72
+ rb_ary_push(ret, rb_str_new2((char *)node->children->content));
73
+ }
74
+ else {
75
+ rb_raise(rb_eRuntimeError,
76
+ "unsupported node type in node set: %d", node->type);
77
+ }
78
+ }
79
+ xmlBufferFree(buff);
80
+ }
81
+ break;
82
+ case XPATH_BOOLEAN:
83
+ ret = obj->boolval ? Qtrue : Qfalse;
84
+ break;
85
+ case XPATH_NUMBER:
86
+ ret = rb_float_new(obj->floatval);
87
+ break;
88
+ case XPATH_STRING:
89
+ ret = rb_str_new2((char *)obj->stringval);
90
+ break;
91
+ /* these cases were in libxslt's python bindings, but i
92
+ don't know what they are, so i'll leave them alone */
93
+ case XPATH_XSLT_TREE:
94
+ case XPATH_POINT:
95
+ case XPATH_RANGE:
96
+ case XPATH_LOCATIONSET:
97
+ default:
98
+ rb_raise(rb_eTypeError,
99
+ "can't convert XPath object of type %d to Ruby object", obj->type);
100
+ }
101
+ xmlXPathFreeObject(obj);
102
+ return ret;
103
+ }
104
+
105
+ xmlXPathObjectPtr
106
+ value2xpathObj(VALUE val) {
107
+ xmlXPathObjectPtr ret = NULL;
108
+
109
+ switch (TYPE(val)) {
110
+ case T_TRUE:
111
+ case T_FALSE:
112
+ ret = xmlXPathNewBoolean(RTEST(val));
113
+ break;
114
+ case T_FIXNUM:
115
+ case T_FLOAT:
116
+ ret = xmlXPathNewFloat(NUM2DBL(val));
117
+ break;
118
+ case T_STRING:
119
+ ret = xmlXPathWrapString(xmlStrdup((const xmlChar *)StringValuePtr(val)));
120
+ break;
121
+ case T_NIL:
122
+ ret = xmlXPathNewNodeSet(NULL);
123
+ break;
124
+ case T_ARRAY: {
125
+ int i, j;
126
+ ret = xmlXPathNewNodeSet(NULL);
127
+
128
+ for (i = RARRAY_LEN(val); i > 0; i--) {
129
+ xmlXPathObjectPtr obj = value2xpathObj(rb_ary_shift(val));
130
+ if ((obj->nodesetval != NULL) && (obj->nodesetval->nodeNr != 0)) {
131
+ for (j = 0; j < obj->nodesetval->nodeNr; j++) {
132
+ xmlXPathNodeSetAdd(ret->nodesetval, obj->nodesetval->nodeTab[j]);
133
+ }
134
+ }
135
+ }
136
+ break;
137
+ }
138
+ default:
139
+ rb_raise(rb_eTypeError,
140
+ "can't convert object of type %s to XPath object", rb_obj_classname(val));
141
+ }
142
+
143
+ return ret;
144
+ }
145
+
146
+ void
147
+ xmlXPathFuncCallback(xmlXPathParserContextPtr ctxt, int nargs) {
148
+ VALUE ns_hash, func_hash, args[nargs], block;
149
+ const xmlChar *namespace, *name;
150
+ int i;
151
+
152
+ if (ctxt == NULL || ctxt->context == NULL) {
153
+ return;
154
+ }
155
+
156
+ namespace = ctxt->context->functionURI;
157
+ name = ctxt->context->function;
158
+
159
+ if (
160
+ (ns_hash = rb_ivar_get(cXSLT, rb_intern("@registry"))) == Qnil ||
161
+ (func_hash = rb_hash_aref(ns_hash, rb_str_new2((char *)namespace))) == Qnil
162
+ ) {
163
+ rb_raise(rb_eArgError, "namespace %s not registered!\n", namespace);
164
+ }
165
+
166
+ if ((block = rb_hash_aref(func_hash, rb_str_new2((char *)name))) == Qnil) {
167
+ rb_raise(rb_eArgError, "name %s not registered!\n", name);
168
+ }
169
+
170
+ for (i = nargs - 1; i >= 0; i--) {
171
+ args[i] = xpathObj2value(valuePop(ctxt), ctxt->context->doc);
172
+ }
173
+
174
+ valuePush(ctxt, value2xpathObj(
175
+ rb_funcall2(block, rb_intern("call"), nargs, args)));
176
+ }
177
+
178
+ static VALUE
179
+ ruby_xslt_register(VALUE class, VALUE namespace, VALUE name) {
180
+ VALUE ns_hash, func_hash;
181
+
182
+ if (!rb_block_given_p()) {
183
+ rb_raise(rb_eArgError, "no block given");
184
+ }
185
+
186
+ if ((ns_hash = rb_ivar_get(cXSLT, rb_intern("@registry"))) == Qnil) {
187
+ ns_hash = rb_ivar_set(cXSLT, rb_intern("@registry"), rb_hash_new());
188
+ }
189
+
190
+ if ((func_hash = rb_hash_aref(ns_hash, namespace)) == Qnil) {
191
+ func_hash = rb_hash_aset(ns_hash, namespace, rb_hash_new());
192
+ }
193
+
194
+ rb_hash_aset(func_hash, name, rb_block_proc());
195
+
196
+ xsltRegisterExtModuleFunction(
197
+ BAD_CAST StringValuePtr(name), BAD_CAST StringValuePtr(namespace), xmlXPathFuncCallback);
198
+
199
+ return Qnil;
200
+ }
201
+
202
+ void
203
+ Init_libxslt_ruby(void) {
204
+ LIBXML_TEST_VERSION;
205
+
206
+ cLibXSLT = rb_define_module("LibXSLT");
207
+ cXSLT = rb_define_module_under(cLibXSLT, "XSLT");
208
+
209
+ cXMLDocument = rb_const_get(rb_const_get(rb_const_get(rb_cObject,
210
+ rb_intern("LibXML")), rb_intern("XML")), rb_intern("Document"));
211
+
212
+ rb_define_singleton_method(cXSLT, "register", ruby_xslt_register, 2);
213
+
214
+ rb_define_const(cXSLT, "MAX_DEPTH", INT2NUM(xsltMaxDepth));
215
+ rb_define_const(cXSLT, "MAX_SORT", INT2NUM(XSLT_MAX_SORT));
216
+ rb_define_const(cXSLT, "ENGINE_VERSION", rb_str_new2(xsltEngineVersion));
217
+ rb_define_const(cXSLT, "LIBXSLT_VERSION", INT2NUM(xsltLibxsltVersion));
218
+ rb_define_const(cXSLT, "LIBXML_VERSION", INT2NUM(xsltLibxmlVersion));
219
+ rb_define_const(cXSLT, "XSLT_NAMESPACE", rb_str_new2((const char*)XSLT_NAMESPACE));
220
+ rb_define_const(cXSLT, "DEFAULT_VENDOR", rb_str_new2(XSLT_DEFAULT_VENDOR));
221
+ rb_define_const(cXSLT, "DEFAULT_VERSION", rb_str_new2(XSLT_DEFAULT_VERSION));
222
+ rb_define_const(cXSLT, "DEFAULT_URL", rb_str_new2(XSLT_DEFAULT_URL));
223
+ rb_define_const(cXSLT, "NAMESPACE_LIBXSLT", rb_str_new2((const char*)XSLT_LIBXSLT_NAMESPACE));
224
+ rb_define_const(cXSLT, "NAMESPACE_NORM_SAXON", rb_str_new2((const char*)XSLT_NORM_SAXON_NAMESPACE));
225
+ rb_define_const(cXSLT, "NAMESPACE_SAXON", rb_str_new2((const char*)XSLT_SAXON_NAMESPACE));
226
+ rb_define_const(cXSLT, "NAMESPACE_XT", rb_str_new2((const char*)XSLT_XT_NAMESPACE));
227
+ rb_define_const(cXSLT, "NAMESPACE_XALAN", rb_str_new2((const char*)XSLT_XALAN_NAMESPACE));
228
+
229
+ eXSLTError = rb_define_class_under(cLibXSLT, "XSLTError", rb_eRuntimeError);
230
+
231
+ ruby_init_xslt_stylesheet();
232
+
233
+ /* Now load exslt. */
234
+ exsltRegisterAll();
235
+ }
@@ -0,0 +1,32 @@
1
+ /* $Id: libxslt.h 43 2007-12-07 12:38:59Z transami $ */
2
+
3
+ /* Please see the LICENSE file for copyright and distribution information */
4
+
5
+ #ifndef __RUBY_LIBXSLT_H__
6
+ #define __RUBY_LIBXSLT_H__
7
+
8
+ #include <ruby.h>
9
+ #include <rubyio.h>
10
+ #include <libxml/parser.h>
11
+ #include <libxml/debugXML.h>
12
+ #include <libxslt/extra.h>
13
+ #include <libxslt/xslt.h>
14
+ #include <libxslt/xsltInternals.h>
15
+ #include <libxslt/transform.h>
16
+ #include <libxslt/xsltutils.h>
17
+ #include <libexslt/exslt.h>
18
+
19
+ #include "ruby_xslt_stylesheet.h"
20
+
21
+ #include "version.h"
22
+
23
+ /*#if ((RUBY_LIBXML_VER_MAJ != RUBY_LIBXSLT_VER_MAJ) || (RUBY_LIBXML_VER_MIN != RUBY_LIBXSLT_VER_MIN))
24
+ #error "Incompatible LibXML-Ruby headers - please install same major/micro version"
25
+ #endif*/
26
+
27
+ extern VALUE cLibXSLT;
28
+ extern VALUE cXSLT;
29
+ extern VALUE eXSLTError;
30
+ extern VALUE cXMLDocument;
31
+
32
+ #endif
@@ -0,0 +1,271 @@
1
+ /* $Id: ruby_xslt_stylesheet.c 42 2007-12-07 06:09:35Z transami $ */
2
+
3
+ /* See the LICENSE file for copyright and distribution information. */
4
+
5
+ #include "libxslt.h"
6
+ #include "ruby_xslt_stylesheet.h"
7
+
8
+ /*
9
+ * Document-class: LibXSLT::XSLT::Stylesheet
10
+ *
11
+ * The XSLT::Stylesheet represents a XSL stylesheet that
12
+ * can be used to transform an XML document. For usage information
13
+ * refer to XSLT::Stylesheet#apply
14
+ *
15
+ */
16
+
17
+ VALUE cXSLTStylesheet;
18
+
19
+ void
20
+ ruby_xslt_stylesheet_free(xsltStylesheetPtr xstylesheet) {
21
+ xsltFreeStylesheet(xstylesheet);
22
+ }
23
+
24
+ static VALUE
25
+ ruby_xslt_stylesheet_alloc(VALUE klass) {
26
+ return Data_Wrap_Struct(cXSLTStylesheet,
27
+ NULL, ruby_xslt_stylesheet_free,
28
+ NULL);
29
+ }
30
+
31
+
32
+ /* call-seq:
33
+ * XSLT::Stylesheet.new(document) -> XSLT::Stylesheet
34
+ *
35
+ * Creates a new XSLT stylesheet based on the specified document.
36
+ * For memory management reasons, a copy of the specified document
37
+ * will be made, so its best to create a single copy of a stylesheet
38
+ * and use it multiple times.
39
+ *
40
+ * stylesheet_doc = XML::Document.file('stylesheet_file')
41
+ * stylesheet = XSLT::Stylesheet.new(stylesheet_doc)
42
+ *
43
+ */
44
+ static VALUE
45
+ ruby_xslt_stylesheet_initialize(VALUE self, VALUE document) {
46
+ xmlDocPtr xdoc;
47
+ xmlDocPtr xcopy;
48
+ xsltStylesheetPtr xstylesheet;
49
+
50
+ if (!rb_obj_is_kind_of(document, cXMLDocument))
51
+ rb_raise(rb_eTypeError, "Must pass in an XML::Document instance.");
52
+
53
+ /* NOTE!! Since the stylesheet own the specified document, the easiest
54
+ * thing to do from a memory standpoint is too copy it and not expose
55
+ * the copy to Ruby. The other solution is expose a memory management
56
+ * API on the document object for taking ownership of the document
57
+ * and specifying when it has been freed. Then the document class
58
+ * has to be updated to always check and see if the document is
59
+ * still valid. That's all doable, but seems like a pain, so
60
+ * just copy the document for now. */
61
+ Data_Get_Struct(document, xmlDoc, xdoc);
62
+ xcopy = xmlCopyDoc(xdoc, 1);
63
+ xstylesheet = xsltParseStylesheetDoc(xcopy);
64
+ xstylesheet->_private = (void *)self;
65
+ DATA_PTR(self) = xstylesheet;
66
+
67
+ /* Save a reference to the document as an attribute accessable to ruby */
68
+ return self;
69
+ }
70
+
71
+ /* Helper method to convert Ruby params to C params */
72
+ char **
73
+ ruby_xslt_coerce_params(VALUE params) {
74
+ char** result;
75
+ size_t length;
76
+ size_t i;
77
+
78
+ length = RARRAY_LEN(params);
79
+ result = ALLOC_N(char *, length + 2);
80
+
81
+ for (i=0; i<length; i++) {
82
+ VALUE str = rb_String(RARRAY_PTR(params)[i]);
83
+ int strLen = RSTRING_LEN(str);
84
+ result[i] = ALLOC_N(char, strLen + 1);
85
+ memset(result[i], 0, strLen + 1);
86
+ strncpy(result[i], RSTRING_PTR(str), strLen);
87
+ }
88
+
89
+ /* Null terminate the array - need to empty elements */
90
+ result[i] = NULL;
91
+ result[i+1] = NULL;
92
+
93
+ return result;
94
+ }
95
+
96
+
97
+ /* call-seq:
98
+ * stylesheet.apply(document, {params}) -> XML::Document
99
+ *
100
+ * Apply this stylesheet transformation to the provided document.
101
+ * This method may be invoked multiple times.
102
+ *
103
+ * Params:
104
+ * * document - An instance of an XML::Document
105
+ * * params - An optional hash table that specifies the values for xsl:param values embedded in the stylesheet.
106
+ *
107
+ * Example:
108
+ *
109
+ * stylesheet_doc = XML::Document.file('stylesheet_file')
110
+ * stylesheet = XSLT::Stylesheet.new(stylesheet_doc)
111
+ *
112
+ * xml_doc = XML::Document.file('xml_file')
113
+ * result = stylesheet.apply(xml_doc)
114
+ * result = stylesheet.apply(xml_doc, {:foo => 'bar'})
115
+ */
116
+ static VALUE
117
+ ruby_xslt_stylesheet_apply(int argc, VALUE *argv, VALUE self) {
118
+ xmlDocPtr xdoc;
119
+ xsltStylesheetPtr xstylesheet;
120
+ xmlDocPtr result;
121
+ VALUE document;
122
+ VALUE params;
123
+ int i;
124
+
125
+ char** pParams;
126
+
127
+ if (argc > 2 || argc < 1)
128
+ rb_raise(rb_eArgError, "wrong number of arguments (need 1 or 2)");
129
+
130
+ document = argv[0];
131
+
132
+ if (!rb_obj_is_kind_of(document, cXMLDocument))
133
+ rb_raise(rb_eTypeError, "Must pass in an XML::Document instance.");
134
+
135
+ /* Make sure params is a flat array */
136
+ params = (argc == 2 ? argv[1]: Qnil);
137
+ params = rb_Array(params);
138
+ rb_funcall(params, rb_intern("flatten!"), 0);
139
+ pParams = ruby_xslt_coerce_params(params);
140
+
141
+ Data_Get_Struct(document, xmlDoc, xdoc);
142
+ Data_Get_Struct(self, xsltStylesheet, xstylesheet);
143
+
144
+ result = xsltApplyStylesheet(xstylesheet, xdoc, (const char**)pParams);
145
+
146
+ if (!result)
147
+ rb_raise(eXSLTError, "Transformation failed");
148
+
149
+ /* Free allocated array of *chars. Note we don't have to
150
+ free the last array item since its set to NULL. */
151
+ for (i=0; i<RARRAY_LEN(params); i++) {
152
+ ruby_xfree(pParams[i]);
153
+ }
154
+ ruby_xfree(pParams);
155
+
156
+ return rxml_document_wrap(result);
157
+ }
158
+
159
+
160
+ /* call-seq:
161
+ * sheet.debug(to = $stdout) => (true|false)
162
+ *
163
+ * Output a debug dump of this stylesheet to the specified output
164
+ * stream (an instance of IO, defaults to $stdout). Requires
165
+ * libxml/libxslt be compiled with debugging enabled. If this
166
+ * is not the case, a warning is triggered and the method returns
167
+ * false.
168
+ */
169
+ /*VALUE
170
+ ruby_xslt_stylesheet_debug(int argc, VALUE *argv, VALUE self) {
171
+ #ifdef LIBXML_DEBUG_ENABLED
172
+ OpenFile *fptr;
173
+ VALUE io;
174
+ FILE *out;
175
+ rxml_document_t *parsed;
176
+ ruby_xslt_stylesheet *xss;
177
+
178
+ Data_Get_Struct(self, ruby_xslt_stylesheet, xss);
179
+ if (NIL_P(xss->parsed))
180
+ rb_raise(eXMLXSLTStylesheetRequireParsedDoc, "must have a parsed XML result");
181
+
182
+ switch (argc) {
183
+ case 0:
184
+ io = rb_stdout;
185
+ break;
186
+ case 1:
187
+ io = argv[0];
188
+ if (rb_obj_is_kind_of(io, rb_cIO) == Qfalse)
189
+ rb_raise(rb_eTypeError, "need an IO object");
190
+ break;
191
+ default:
192
+ rb_raise(rb_eArgError, "wrong number of arguments (0 or 1)");
193
+ }
194
+
195
+ Data_Get_Struct(xss->parsed, rxml_document_t, parsed);
196
+ if (parsed->doc == NULL)
197
+ return(Qnil);
198
+
199
+ GetOpenFile(io, fptr);
200
+ rb_io_check_writable(fptr);
201
+ out = GetWriteFile(fptr);
202
+ xmlDebugDumpDocument(out, parsed->doc);
203
+ return(Qtrue);
204
+ #else
205
+ rb_warn("libxml/libxslt was compiled without debugging support. Please recompile libxml/libxslt and their Ruby modules");
206
+ return(Qfalse);
207
+ #endif
208
+ }
209
+ */
210
+
211
+ /* TODO should this automatically apply the sheet if not already,
212
+ given that we're unlikely to do much else with it? */
213
+
214
+ /* call-seq:
215
+ * sheet.print(to = $stdout) => number_of_bytes
216
+ *
217
+ * Output the result of the transform to the specified output
218
+ * stream (an IO instance, defaults to $stdout). You *must* call
219
+ * +apply+ before this method or an exception will be raised.
220
+ */
221
+ /*VALUE
222
+ ruby_xslt_stylesheet_print(int argc, VALUE *argv, VALUE self) {
223
+ OpenFile *fptr;
224
+ VALUE io;
225
+ FILE *out;
226
+ rxml_document_t *parsed;
227
+ ruby_xslt_stylesheet *xss;
228
+ int bytes;
229
+
230
+ Data_Get_Struct(self, ruby_xslt_stylesheet, xss);
231
+ if (NIL_P(xss->parsed))
232
+ rb_raise(eXMLXSLTStylesheetRequireParsedDoc, "must have a parsed XML result");
233
+
234
+ switch (argc) {
235
+ case 0:
236
+ io = rb_stdout;
237
+ break;
238
+ case 1:
239
+ io = argv[0];
240
+ if (rb_obj_is_kind_of(io, rb_cIO) == Qfalse)
241
+ rb_raise(rb_eTypeError, "need an IO object");
242
+ break;
243
+ default:
244
+ rb_raise(rb_eArgError, "wrong number of arguments (0 or 1)");
245
+ }
246
+
247
+ Data_Get_Struct(xss->parsed, rxml_document_t, parsed);
248
+ if (parsed->doc == NULL)
249
+ return(Qnil);
250
+
251
+ GetOpenFile(io, fptr);
252
+ rb_io_check_writable(fptr);
253
+ out = GetWriteFile(fptr);
254
+ bytes = xsltSaveResultToFile(out, parsed->doc, xss->xsp);
255
+
256
+ return(INT2NUM(bytes));
257
+ }*/
258
+
259
+
260
+ #ifdef RDOC_NEVER_DEFINED
261
+ cLibXSLT = rb_define_module("LibXSLT");
262
+ cXSLT = rb_define_module_under(cLibXSLT, "XSLT");
263
+ #endif
264
+
265
+ void
266
+ ruby_init_xslt_stylesheet(void) {
267
+ cXSLTStylesheet = rb_define_class_under(cXSLT, "Stylesheet", rb_cObject);
268
+ rb_define_alloc_func(cXSLTStylesheet, ruby_xslt_stylesheet_alloc);
269
+ rb_define_method(cXSLTStylesheet, "initialize", ruby_xslt_stylesheet_initialize, 1);
270
+ rb_define_method(cXSLTStylesheet, "apply", ruby_xslt_stylesheet_apply, -1);
271
+ }