I have a few Saxon/XSLT extension functions that I created through the simple interface that I register like this:
Processor proc = new Processor(false);
proc.registerExtensionFunction(myExtensionFunctionInstance);
Now I want to use that with BaseX, but I see that BaseX instantiates its own Processor instance through JAXP APIs:
// Somewhere inside basex's XSLT transformer instantiation (see link above).
final TransformerFactory tf = TransformerFactory.newInstance();
So it seems I need to find a way to change the factory that BaseX uses such that I can register my functions.
If I get it right I need to create my own factory implementation. Adapted from Saxon's net.sf.saxon.TransformerFactoryImpl:
package my.package;
public class MyOwnTrasnformerFactory extends SaxonTransformerFactory {
public TransformerFactoryImpl() {
super();
ExtensionFunction myFunction = new MyExtFunction(...);
this.processor.registerExtensionFunction(myFunction);
}
public TransformerFactoryImpl(Configuration config) {
super(config);
ExtensionFunction myFunction = new MyExtFunction(...);
this.processor.registerExtensionFunction(myFunction);
}
}
And then I could do:
System.setProperty(
"javax.xml.transform.TransformerFactory",
"my.package.MyOwnTrasnformerFactory");
I ended up implementing the solution described on my question... here's a full gist:
https://gist.github.com/EmmanuelOga/018ece8b4776636ce1ab3f73e215ce75
The relevant parts:
(ns rainbowfish.xslt-factory
(:import [net.sf.saxon Configuration]
[net.sf.saxon.lib Feature])
(:gen-class
:name rainbowfish.XsltFactory
:extends net.sf.saxon.jaxp.SaxonTransformerFactory
:post-init configure))
(defn -configure
"Generated Java class will call this method right after
construction. Here we get a chance to grab Saxon's processor for
configuration purposes (for example, adding extension functions)."
[this & args]
(let [processor (.getProcessor this)]
;; Here we can do whatever we want to the processor,
;; For instance, register some extension functions.
(doto processor
(.registerExtensionFunction some-function))))
That defines the Java class rainbowfish.XsltFactory
using clojure. Since it uses gen-class, it needs to be compiled by calling compile('rainbowfish.xslt-factory)
or by asking lein to compile it, etc.
Now that we have a class that configures the XSLT processor, we need to inform BaseX that it should use it. BaseX reads the JAXP configuration for this. The configuration can be set like this:
(System/setProperty
"javax.xml.transform.TransformerFactory",
"rainbowfish.XsltFactory")
... this should be called before creating an instance of the BaseX server. See the gist for more details!