blackwinter-libxslt-ruby 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }