Init error with Saxon HE Java Extension function in Oxygen XML

92 views Asked by At

I'm trying to learn how to create extension functions for XSLT using Java & Saxon-HE using the ExtensionFunctionDefinition class. So far, I've been able to extract and then re-compile a .jar using the Oxygen DITA-OT plugin to convert Latex to SVG. That works fine when I add the necessary jar files as extensions and transforms the input string as expected.

I feel like the issue has to be something extremely simple (and I'm very inexperienced when it comes to writing Java language, admittedly).

However, when I make some fairly simple modifications to the Java code to simply concatenate a string together, I get very nondescript error: E [Saxon-HE 11.4] TestPackage.TestClass.<init>().

Here is my Java:

package TestPackage;

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;

public class TestClass extends ExtensionFunctionDefinition {
    @Override
    public StructuredQName getFunctionQName() {
        return new StructuredQName("ex-ns", "http://www.example.com/test-class", "function");
    }
    
    @Override
    public SequenceType[] getArgumentTypes() {
        return new SequenceType[] { SequenceType.SINGLE_STRING };
    }
    
    @Override
    public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
        return SequenceType.VOID;
    }
    
    @Override
    public ExtensionFunctionCall makeCallExpression() {
        return new ExtensionFunctionCall() {
            @Override
            public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
                String str1 = "Hello ";
                String str2 = "!";
                String first = arguments[0].head().getStringValue();
                String result = str1.concat(first).concat(str2);
                return StringValue.makeStringValue(result);
            }
        };
    }
}

and here is my stylesheet for the transformation:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:saxon="http://saxon.sf.net/"
    xmlns:ex-ns="http://www.example.com/test-class">
    
    <xsl:template match="/doc">
        <xsl:message>Doc here!</xsl:message>
        <doc>
            <xsl:apply-templates/>
        </doc>
    </xsl:template>
    
    <xsl:template match="//name">
        <xsl:variable name="first-name" as="xs:string" select="@first"/>
        <xsl:variable name="last-name" as="xs:string" select="@last"/>
        <name>
            <xsl:copy-of select="mine:function($first-name)"/>
        </name>
    </xsl:template>
</xsl:stylesheet>

The input XML file is a very simple structure (as you can imagine) of a element containing some <name first="example"/> child elements. I would expect each element to be transformed to Hello example!, but instead I simply get the above error. Further, it appears that Saxon-HE (or any Saxon edition, it doesn't seem to matter) gives this error upon reading the jar file before any transformations. That is, the message "Doc here!" is never produced before the error is given.

UPDATED Corrected Java based on comments below (snippet). Use a public class and standard formats for the StructuredQName():

public class TestClass extends ExtensionFunctionDefinition {
    @Override
    public StructuredQName getFunctionQName() {
        return new StructuredQName("ex-ns", "http://www.example.com/test-class", "function");
    }

Corrected XSLT to use with this correction. Note how the xmlns uses the values declared in the StructureQName:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:saxon="http://saxon.sf.net/"
    xmlns:ex-ns="http://www.example.com/test-class">

...
        <name>
            <xsl:copy-of select="ex-ns:function($first-name)"/>
        </name>
2

There are 2 answers

1
Michael Kay On BEST ANSWER

My first thought was that you have simply failed to register the ExtensionFunctionDefinition with the Saxon Processor, for example by calling Processor.registerExtensionFunction(). But I think that would give you different diagnostics.

For diagnosing this kind of problem, the -TJ option on the command line can be helpful.

I think you have confused matters by using a URI in your function name, "java:TestPackage.TestClass" that indicates this to be a reflexive extension function. I don't think this is the primary problem, but it might lead to confusing diagnostics.

In addition, you have declared the function to return SequenceType.VOID, but your stylesheet is trying to use the result, which will fail. The function should be changed to declare its actual result type, SequenceType.SINGLE_STRING.

1
Radu Coravu On

To add to what Michael Kay said, your Java class should also be declared as public. As it is now, it can be accessed only from classes in the same package.