views:

140

answers:

3

I am using Xalan 2.7.0 (as bundled with Apache FOP 1.0) and have problems when using string functions.

The line <xsl:value-of select="fn:replace('test', 't', '*')"/> results in this exception:

javax.xml.transform.TransformerException: java.lang.IllegalArgumentException: argument type mismatch
    at org.apache.fop.cli.InputHandler.transformTo(InputHandler.java:302)

<xsl:value-of select="fn:string-length('foobar')"/> results in:

javax.xml.transform.TransformerException: java.lang.NoSuchMethodException: For extension function, could not find method java.lang.String.stringLength([ExpressionContext,] ).
    at org.apache.fop.cli.InputHandler.transformTo(InputHandler.java:302)

Now this is weird! Why does Xalan look for a stringLength function on java.lang.String? I tested <xsl:value-of select="fn:substring('foobar', 2, 3)"/>, and indeed: the result is o, so the arguments were used as startIndex, endIndex (like java.lang.String.substring()) instead of XPath's fn:substring(string, start, length) funcion.

So I think that Xalan is somehow missing its XPath function library and using the regular String class instead. I confirmed this by calling the non-existing function fn:index-of('foobar', 'bar') which works fine and maps to java.lang.String.indexOf(str).

Why does Xalan do that? And how can I fix it?

System info: Xalan uses the standard Mac OS X 10.6.4 Java version, 1.6.0_20.

Update

Okay, leave aside the replace() function for a moment. Shouldn't Xalan, being an XSLT 1.0 processor, implement the XPath 1.0 function substring (string, startIndex, length) and not the (string, startIndex, endIndex) function I see in my expirements? Is it coincidence that this startIndex, endIndex function looks like the substring method of java.lang.String?

And why do I get a NoSuchMethodError when I use the fn:string-length function?

Something's wrong here, and it clearly isn`t about XPath 1.0 vs 2.0...

A: 

replace() is an XSLT 2.0 function. Xalan is an XSLT 1.0 processor.

You can simulate the replace() function with a template like this from @Ektron Doug D:

<xsl:template name="replace-substring">
<xsl:param name="original"/>
<xsl:param name="substring"/>
<xsl:param name="replacement" select="''"/>
<xsl:choose>
    <xsl:when test="contains($original, $substring)">
        <xsl:value-of select="substring-before($original, $substring)"/>
        <xsl:copy-of select="$replacement"/>
        <xsl:call-template name="replace-substring">
            <xsl:with-param name="original" select="substring-after($original, $substring)"/>
            <xsl:with-param name="substring" select="$substring"/>
            <xsl:with-param name="replacement" select="$replacement"/>
        </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="$original"/>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

Keep in mind that this XSLT 1.0 solution is a very simple find/replace. The XSLT 2.0 function replace() can use REGEX patterns for the "find" expression.

Mads Hansen
Last night I upvoted your answer and this morning it has `0` rep -- somebody has downvoted both you and me :(
Dimitre Novatchev
+1  A: 

The result from substring('foobar', 2, 3) (Do note: without namespace) should be oob.

In XSLT 1.0 any function call with prefix will be interpreted as an extension call. From http://www.w3.org/TR/xslt#section-Extension-Functions

If a FunctionName in a FunctionCall expression is not an NCName (i.e. if it contains a colon), then it is treated as a call to an extension function. The FunctionName is expanded to a name using the namespace declarations from the evaluation context.

Alejandro
+1  A: 

Now this is weird! Why does Xalan look for a stringLength function on java.lang.String? I tested , and indeed: the result is o, so the arguments were used as startIndex, endIndex (like java.lang.String.substring()) instead of XPath's fn:substring(string, start, length) funcion.

So I think that Xalan is somehow missing its XPath function library and using the regular String class instead. I confirmed this by calling the non-existing function fn:index-of('foobar', 'bar') which works fine and maps to java.lang.String.indexOf(str).

Why does Xalan do that? And how can I fix it?

  1. Why does Xalan do that? Because Xalan is XSLT 1.0 processor and any compliant XSLT 1.0 processor only supports XPath 1.0. replace() is a standard function of XPath 2.0 and must be implemented in any compliant XSLT 2.0 processor.

  2. And how can I fix it? By using an XSLT 2.0 processor like Saxon 9.x or AltovaXML2010. Or write a named, recursive <xsl:template> to do simple replacements in XSLT 1.0.

Dimitre Novatchev