As XProc uses XPath (current version 2.0) as expression language, you can use all functions defined in the XPath standard in your XProc pipelines. This gives you a powerful way to create expressions, but sometimes it comes to its limits:
- You have to write the same (long) XPath expression over and over again on different places in the same pipeline (especially in p:choose) or in different pipelines. This is both boring and error-prone, but since there is no possibility to declare your own functions in XProc, there is no way out with standard XProc syntax.
- Sometimes you would like to use functions, that are not even covered by XPath 2.0's function library and cannot be expressed by combining these functions. This is getting better with XPath 3.0's much larger function library, but whenever there is a standard library, at one time or the other you will think of a function, not expressible using the functions in the standard.
MorganaXProc offers three ways to use self defined functions in your XProc pipelines:
- Importing functions from XQuery modules,
- Importing functions from XSLT stylesheets and
- Importing functions from Java packages.
Before we discuss these ways of declaring your own function for MorganaXProc, there is a caveat: Using self defined functions in your pipelines will make them unusable on other XProc processors and make it harder to deploy your pipelines to other users. But keeping that in mind, let us see how to do it.
Supposing you are somewhat familiar with XQuery, you will know about XQuery library modules: You can declare variables and functions in a library module and import it into the main module of your query. You have to declare a target namespace for the library module and make sure all of the module's functions and variables are in that namespace. The target namespace is also used to import the library module's functions and variables into your main module.
To import the functions (not the variables!) from an XQuery library module into your XProc pipelines is fairly easy with MorganaXProc (starting from Rel. 0.95-8):
where prefix "mox" is bound to MorganaXProc's namespace "http://www.xml-project.com/morgana". You can now call all functions declared in the imported library module in the XPath expressions of pipeline by using their qualified name, so make sure you have declared the module's target namespace in your XProc pipeline.
Let us look at this in some more detail: The first thing you might have noticed is the attribute "content-type", that is not defined in Recommendation 11 May 2010. It allows you to specify the mime type for the resource to import. The default value for attribute "content-type" is XProc mime type "application/xproc+xml", which will import an XProc library as you might expect. To import an XQuery module, you have to use XQuery mime type "application/xquery" as seen in the example above.
Now, what about the attribute "href" when importing an XQuery module? You can give the uri of the query module to be imported and MorganaXProc will use the same mechanism to obtain the source you know from importing XProc step libraries: If the uri is relative, it is made absolute by using the base-uri of "p:import". This also means that MorganaXProc's security mechanism is applied to importing XQuery modules in the same way as it is used for importing pipelines: You have to allow the import of an XQuery module in the same way as allowing an XProc step library to be imported.
If you want to import the XQuery functions from MorganaXProc's implementation of the EXPath packaging system, the import is somewhat different as the packaging system uses the target namespace to identify the module to be imported. Therefore you have to specify the target namespace of the module to be imported as value for attribute "href" in the XProc import statement.
In addition to extension attribute "mox:content-type" MorganaXProc uses the optional extension attribute "mox:namespace" allowing you to make sure, the target namespace of the imported XQuery module is as expected. Just specify the namespace you expect on "mox:namespace" and MorganaXProc will raise a static error (mox:S0002) if the imported module does not have the specified target namespace.
If MorganaXProc is unable to import the XQuery module, either because it cannot resolve the module's source or because the import is not allowed by MorganaXProc's security system, a static error (mox:S0001) is raised.
Two final remarks:
- The XQuery functions imported into your XProc pipeline are only visible in XPath expression evaluated by XProc itself, not in the XPath expression in "p:xslt" or "p:xquery". (Of course you can add an import to your query to make the functions visible.)
- The XQuery functions are always evaluated by MorganaXProc's built-in XQuery processor, so choosing another XQuery processor for "p:xquery" does not apply to function import. This means you cannot use any extensions implemented by the chosen third-party XQuery processor in your functions.
Importing functions defined in an XSLT stylesheet into XProc's function contexts works exactly the same way described above for xquery functions. Starting from MorganaXProc 1.0 you can write
to import all functions from the stylesheet into XProc's function context. If your stylesheet includes or imports other stylesheets, those functions are imported as well. Since functions are available since XSLT 2.0, you have to use an XSLT processor supporting at least XSLT 2.0. (See here for instructions to select your favourite XSLT processor in MorganaXProc.)
As with xquery modules the value given for "href" is checked against the security system and may related to a resource in an EXPath package or related to an XMLCatalog.
If the given resource is not found or can be compiled using the selected XSLT compiler, a new error "mox:S0005" is raised.
Using XQuery functions in XProc makes it a lot easier to write effective pipelines, but it also has its obvious limits because there are a lot of use cases, where you cannot express a function using XQuery. As we said above, with XPath 3.0 there is a much improved and enlarged function library, but there will be still cases, where you will wish for some other way to express your functions.
As MorganaXProc is written in Java, the most obvious alternative to XQuery functions are Java classes implementing the XPath functions you need. You can find a short introduction on writing Java classes to implement XPath functions in the A Guide to MorganaXProc’s API. For now let us suppose, you have obtained a Java class implementing an XPath function. How to use them in the XPath expressions of your XProc pipeline? It is as easy as this:
As you can see, we again use the extension attribute "mox:content-type" to import the functions. As we want to import functions written in Java, we use the mime-type "application/java-archive" to tell MorganaXProc to look into a Java archive (commonly known as "JAR"-file) and import the functions found there. To tell MorganaXProc from which class we want to import the functions, we use attribute "href" by giving the fully qualified name of the java class (the full name of the package the class belongs to, and the name of the class itself).
Now, what happens: If MorganaXProc's compiler gets to a "p:import" with the java archive mime-type, it will take the value of attribute "href" to obtain the named class from a ".jar"-files in folder "Extensions" next to "MorganaXProc.jar" on your local file system. In fact, MorganaXProc will look into all ".jar"-files in folder "Extensions" to find the Java class with the given fully qualified name. So the name of the ".jar"-file does not matter, MorganaXProc will take the first class matching the given class name. And: MorganaXProc expects the found class to implement interface "com.xml_project.morganaxproc.extensions.FunctionPackage". If MorganaXProc is unable to find a class with these properties, static error "mox:S0003" is raised.
If everything went right, you can now use all functions declared in the function package in the XPath expressions of your XProc pipelines. To call them, you have to use the namespace declared in the function package, so make sure you have this namespace also declared in your pipeline. To make the namespace of the imported functions more obvious when looking at the pipeline, you can -as with XQuery functions- use extension attribute "mox:namespace" to flag the namespace of the functions. This will also tell MorganaXProc just to import the function if they are declared in this namespace. If any other namespace is found, static error "mox:S0002" is raised.
The scope of the imported functions is limited to XPath expressions in XProc itself, so the functions are not visible either in p:xquery or p:xslt. If you need a function to be visible in XProc pipelines, XQuery expressions within XProc and also in XSLT stylesheet within p:xslt, you can load this function globally by using property "ExtensionLibrary" in configuration. This will make the function useable without further import in the XPath expressions of XProc and also in p:xquery (only with MorganaXQueryConnector and SaxonXQueryConnector) and in p:xslt (only with SaxonXSLTConnector). Please note that the function will have no access to the dynamic and static context in XQuery and XSLT.
The last thing to keep in mind is that using XPath functions obtained from a java package is controlled by the security system built in to MorganaXProc. In order to use Java code in your XProc pipeline, you have to set security system's property "JavaLoadAllowed" to true. If you try to import Java functions without the property set to true, MorganaXProc's compiler will raise static error "mox:S0003".