File: //usr/local/rvm/gems/default/gems/nokogiri-1.12.5-x86_64-linux/ext/nokogiri/xslt_stylesheet.c
#include <nokogiri.h>
VALUE cNokogiriXsltStylesheet ;
static void
mark(nokogiriXsltStylesheetTuple *wrapper)
{
rb_gc_mark(wrapper->func_instances);
}
static void
dealloc(nokogiriXsltStylesheetTuple *wrapper)
{
xsltStylesheetPtr doc = wrapper->ss;
NOKOGIRI_DEBUG_START(doc);
xsltFreeStylesheet(doc); /* commented out for now. */
NOKOGIRI_DEBUG_END(doc);
free(wrapper);
}
static void
xslt_generic_error_handler(void *ctx, const char *msg, ...)
{
char *message;
va_list args;
va_start(args, msg);
vasprintf(&message, msg, args);
va_end(args);
rb_str_cat2((VALUE)ctx, message);
free(message);
}
VALUE
Nokogiri_wrap_xslt_stylesheet(xsltStylesheetPtr ss)
{
VALUE self;
nokogiriXsltStylesheetTuple *wrapper;
self = Data_Make_Struct(cNokogiriXsltStylesheet, nokogiriXsltStylesheetTuple,
mark, dealloc, wrapper);
ss->_private = (void *)self;
wrapper->ss = ss;
wrapper->func_instances = rb_ary_new();
return self;
}
/*
* call-seq:
* parse_stylesheet_doc(document)
*
* Parse a stylesheet from +document+.
*/
static VALUE
parse_stylesheet_doc(VALUE klass, VALUE xmldocobj)
{
xmlDocPtr xml, xml_cpy;
VALUE errstr, exception;
xsltStylesheetPtr ss ;
Data_Get_Struct(xmldocobj, xmlDoc, xml);
errstr = rb_str_new(0, 0);
xsltSetGenericErrorFunc((void *)errstr, xslt_generic_error_handler);
xml_cpy = xmlCopyDoc(xml, 1); /* 1 => recursive */
ss = xsltParseStylesheetDoc(xml_cpy);
xsltSetGenericErrorFunc(NULL, NULL);
if (!ss) {
xmlFreeDoc(xml_cpy);
exception = rb_exc_new3(rb_eRuntimeError, errstr);
rb_exc_raise(exception);
}
return Nokogiri_wrap_xslt_stylesheet(ss);
}
/*
* call-seq:
* serialize(document)
*
* Serialize +document+ to an xml string.
*/
static VALUE
serialize(VALUE self, VALUE xmlobj)
{
xmlDocPtr xml ;
nokogiriXsltStylesheetTuple *wrapper;
xmlChar *doc_ptr ;
int doc_len ;
VALUE rval ;
Data_Get_Struct(xmlobj, xmlDoc, xml);
Data_Get_Struct(self, nokogiriXsltStylesheetTuple, wrapper);
xsltSaveResultToString(&doc_ptr, &doc_len, xml, wrapper->ss);
rval = NOKOGIRI_STR_NEW(doc_ptr, doc_len);
xmlFree(doc_ptr);
return rval ;
}
/*
* call-seq:
* transform(document, params = [])
*
* Apply an XSLT stylesheet to an XML::Document.
* +params+ is an array of strings used as XSLT parameters.
* returns Nokogiri::XML::Document
*
* Example:
*
* doc = Nokogiri::XML(File.read(ARGV[0]))
* xslt = Nokogiri::XSLT(File.read(ARGV[1]))
* puts xslt.transform(doc, ['key', 'value'])
*
*/
static VALUE
transform(int argc, VALUE *argv, VALUE self)
{
VALUE xmldoc, paramobj, errstr, exception ;
xmlDocPtr xml ;
xmlDocPtr result ;
nokogiriXsltStylesheetTuple *wrapper;
const char **params ;
long param_len, j ;
int parse_error_occurred ;
rb_scan_args(argc, argv, "11", &xmldoc, ¶mobj);
if (NIL_P(paramobj)) { paramobj = rb_ary_new2(0L) ; }
if (!rb_obj_is_kind_of(xmldoc, cNokogiriXmlDocument)) {
rb_raise(rb_eArgError, "argument must be a Nokogiri::XML::Document");
}
/* handle hashes as arguments. */
if (T_HASH == TYPE(paramobj)) {
paramobj = rb_funcall(paramobj, rb_intern("to_a"), 0);
paramobj = rb_funcall(paramobj, rb_intern("flatten"), 0);
}
Check_Type(paramobj, T_ARRAY);
Data_Get_Struct(xmldoc, xmlDoc, xml);
Data_Get_Struct(self, nokogiriXsltStylesheetTuple, wrapper);
param_len = RARRAY_LEN(paramobj);
params = calloc((size_t)param_len + 1, sizeof(char *));
for (j = 0 ; j < param_len ; j++) {
VALUE entry = rb_ary_entry(paramobj, j);
const char *ptr = StringValueCStr(entry);
params[j] = ptr;
}
params[param_len] = 0 ;
errstr = rb_str_new(0, 0);
xsltSetGenericErrorFunc((void *)errstr, xslt_generic_error_handler);
xmlSetGenericErrorFunc((void *)errstr, xslt_generic_error_handler);
result = xsltApplyStylesheet(wrapper->ss, xml, params);
free(params);
xsltSetGenericErrorFunc(NULL, NULL);
xmlSetGenericErrorFunc(NULL, NULL);
parse_error_occurred = (Qfalse == rb_funcall(errstr, rb_intern("empty?"), 0));
if (parse_error_occurred) {
exception = rb_exc_new3(rb_eRuntimeError, errstr);
rb_exc_raise(exception);
}
return noko_xml_document_wrap((VALUE)0, result) ;
}
static void
method_caller(xmlXPathParserContextPtr ctxt, int nargs)
{
VALUE handler;
const char *function_name;
xsltTransformContextPtr transform;
const xmlChar *functionURI;
transform = xsltXPathGetTransformContext(ctxt);
functionURI = ctxt->context->functionURI;
handler = (VALUE)xsltGetExtData(transform, functionURI);
function_name = (const char *)(ctxt->context->function);
Nokogiri_marshal_xpath_funcall_and_return_values(ctxt, nargs, handler, (const char *)function_name);
}
static void *
initFunc(xsltTransformContextPtr ctxt, const xmlChar *uri)
{
VALUE modules = rb_iv_get(mNokogiriXslt, "@modules");
VALUE obj = rb_hash_aref(modules, rb_str_new2((const char *)uri));
VALUE args = { Qfalse };
VALUE methods = rb_funcall(obj, rb_intern("instance_methods"), 1, args);
VALUE inst;
nokogiriXsltStylesheetTuple *wrapper;
int i;
for (i = 0; i < RARRAY_LEN(methods); i++) {
VALUE method_name = rb_obj_as_string(rb_ary_entry(methods, i));
xsltRegisterExtFunction(ctxt,
(unsigned char *)StringValueCStr(method_name), uri, method_caller);
}
Data_Get_Struct((VALUE)ctxt->style->_private, nokogiriXsltStylesheetTuple,
wrapper);
inst = rb_class_new_instance(0, NULL, obj);
rb_ary_push(wrapper->func_instances, inst);
return (void *)inst;
}
static void
shutdownFunc(xsltTransformContextPtr ctxt,
const xmlChar *uri, void *data)
{
nokogiriXsltStylesheetTuple *wrapper;
Data_Get_Struct((VALUE)ctxt->style->_private, nokogiriXsltStylesheetTuple,
wrapper);
rb_ary_clear(wrapper->func_instances);
}
/*
* call-seq:
* register(uri, custom_handler_class)
*
* Register a class that implements custom XSLT transformation functions.
*/
static VALUE
registr(VALUE self, VALUE uri, VALUE obj)
{
VALUE modules = rb_iv_get(self, "@modules");
if (NIL_P(modules)) { rb_raise(rb_eRuntimeError, "wtf! @modules isn't set"); }
rb_hash_aset(modules, uri, obj);
xsltRegisterExtModule((unsigned char *)StringValueCStr(uri), initFunc, shutdownFunc);
return self;
}
void
noko_init_xslt_stylesheet()
{
rb_define_singleton_method(mNokogiriXslt, "register", registr, 2);
rb_iv_set(mNokogiriXslt, "@modules", rb_hash_new());
cNokogiriXsltStylesheet = rb_define_class_under(mNokogiriXslt, "Stylesheet", rb_cObject);
rb_undef_alloc_func(cNokogiriXsltStylesheet);
rb_define_singleton_method(cNokogiriXsltStylesheet, "parse_stylesheet_doc", parse_stylesheet_doc, 1);
rb_define_method(cNokogiriXsltStylesheet, "serialize", serialize, 1);
rb_define_method(cNokogiriXsltStylesheet, "transform", transform, -1);
}