/* http://xmlsoft.org/XSLT/html/libxslt-extensions.html */
#include "libxslt.h"
/* Helper method to retrieve (and possibly initialize)
the module function registry hash for +namespace+ */
static VALUE
ruby_xslt_module_function_hash(VALUE namespace) {
VALUE ns_hash, func_hash;
if ((ns_hash = rb_ivar_get(cXSLT, rb_intern("@module_function_registry"))) == Qnil) {
ns_hash = rb_ivar_set(cXSLT, rb_intern("@module_function_registry"), rb_hash_new());
}
if ((func_hash = rb_hash_aref(ns_hash, namespace)) == Qnil) {
func_hash = rb_hash_aset(ns_hash, namespace, rb_hash_new());
}
return func_hash;
}
/* Helper method for xsltRegisterExtModuleFunction callback */
static void
ruby_xslt_module_function_callback(xmlXPathParserContextPtr ctxt, int nargs) {
VALUE callback;
VALUE* args = ALLOCA_N(VALUE, nargs);
const xmlChar *namespace, *name;
int i;
if (ctxt == NULL || ctxt->context == NULL) {
return;
}
namespace = ctxt->context->functionURI;
name = ctxt->context->function;
callback = rb_hash_aref(
ruby_xslt_module_function_hash(rb_str_new2((char *)namespace)),
rb_str_new2((char *)name)
);
if (callback == Qnil) {
rb_raise(rb_eArgError, "name `%s' not registered", name);
}
for (i = nargs - 1; i >= 0; i--) {
args[i] = rxml_xpath_to_value(ctxt->context, valuePop(ctxt));
}
valuePush(ctxt, rxml_xpath_from_value(
rb_funcall2(callback, rb_intern("call"), nargs, args)
));
}
/* call-seq:
* XSLT.register_module_function(namespace, name) { ... } -> Proc or nil
*
* Registers +name+ as extension module function in +namespace+ with the
* block as callback. Returns the callback if successful, or +nil+ otherwise.
*
* The callback will be called with whatever XPath expression you pass
* into the function converted to a Ruby object. Its return value will
* be converted to an XPath expression again.
*
* Example:
*
* # register your extension function
* XSLT.register_module_function('http://ex.ns', 'ex-func') { |xp|
* xp.to_a.join('|').upcase
* }
*
* # then use it in your stylesheet
*
* ...
*
*
*
*/
static VALUE
ruby_xslt_register_module_function(VALUE class, VALUE namespace, VALUE name) {
VALUE callback;
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "no block given");
}
if (xsltRegisterExtModuleFunction(
BAD_CAST StringValuePtr(name),
BAD_CAST StringValuePtr(namespace),
ruby_xslt_module_function_callback
) != 0) {
return Qnil;
}
callback = rb_block_proc();
rb_hash_aset(ruby_xslt_module_function_hash(namespace), name, callback);
return callback;
}
/* call-seq:
* XSLT.unregister_module_function(namespace, name) -> Proc or nil
*
* Unregisters +name+ as extension module function in +namespace+.
* Returns the previous callback if successful, or +nil+ otherwise.
*/
static VALUE
ruby_xslt_unregister_module_function(VALUE class, VALUE namespace, VALUE name) {
VALUE func_hash, callback;
func_hash = ruby_xslt_module_function_hash(namespace);
if ((callback = rb_hash_aref(func_hash, name)) == Qnil) {
return Qnil;
}
if (xsltUnregisterExtModuleFunction(
BAD_CAST StringValuePtr(name),
BAD_CAST StringValuePtr(namespace)
) != 0) {
return Qnil;
}
rb_hash_aset(func_hash, name, Qnil);
return callback;
}
/* call-seq:
* XSLT.registered_module_function?(namespace, name) -> true or false
*
* Returns +true+ if +name+ is currently registered as extension module
* function in +namespace+, or +false+ otherwise.
*/
static VALUE
ruby_xslt_registered_module_function_p(VALUE class, VALUE namespace, VALUE name) {
return RTEST(rb_hash_aref(ruby_xslt_module_function_hash(namespace), name));
}
void
ruby_init_exslt() {
/* [HACK] Enclosing classes/modules for RDoc:
* cLibXSLT = rb_define_module("LibXSLT");
* cXSLT = rb_define_module_under(cLibXSLT, "XSLT");
*/
rb_define_singleton_method(cXSLT, "register_module_function", ruby_xslt_register_module_function, 2);
rb_define_singleton_method(cXSLT, "unregister_module_function", ruby_xslt_unregister_module_function, 2);
rb_define_singleton_method(cXSLT, "registered_module_function?", ruby_xslt_registered_module_function_p, 2);
}