Search code examples
javaxmlxsltxsl-foapache-fop

Zero width space in XSL gives stackoverflow error


I'm using FOP 1.1, i've a text which can contain upto 20,000 characters and since there was text which didn't contain any spaces in between have modified the xsl to use the zero width space. It works fine if the xml tag contains around 1500 characters, if it is more than that i'm getting stack overflow error. Heap Space in weblogic server is also 2 GB. Can anyone help on this with a possible workaround or if there is any flaws in the xsl.

Below is the xsl code sample -

    <xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format"
    xmlns:locale="xalan://java.util.Locale" 
    xmlns:arrayList="xalan://java.util.ArrayList" 
    xmlns:func="http://exslt.org/functions" extension-element-prefixes="func"
    version="1.0">

..................




     <fo:block>
        <xsl:value-of select="func:callTemplate(summaryData)"/>
        </fo:block>

..................
    <xsl:template name="zero_width_space_1">
            <xsl:param name="data"/>
            <xsl:param name="counter" select="0"/>
            <xsl:choose>
                <xsl:when test="$counter &lt; (string-length($data))">
                    <xsl:value-of select='concat(substring($data,$counter,1),"&#8203;")'/>
                    <xsl:call-template name="zero_width_space_2">
                        <xsl:with-param name="data" select="$data"/>
                        <xsl:with-param name="counter" select="$counter+1"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>

        <xsl:template name="zero_width_space_2">
            <xsl:param name="data"/>
            <xsl:param name="counter"/>
            <xsl:value-of select='concat(substring($data,$counter,1),"&#8203;")'/>
            <xsl:call-template name="zero_width_space_1">
                <xsl:with-param name="data" select="$data"/>
                <xsl:with-param name="counter" select="$counter+1"/>
            </xsl:call-template>
    </xsl:template>


        <func:function name="func:callTemplate">
            <xsl:param name="string1"/>
            <xsl:call-template name="zero_width_space_1">
                <xsl:with-param name="data" select="$string1"/>
            </xsl:call-template>
            <func:result/>
        </func:function>
</xsl:stylesheet>

Below is the error which i'm getting -

Caused by: java.lang.StackOverflowError at org.apache.xml.utils.FastStringBuffer.append(FastStringBuffer.java:499) at org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.getNodeData(DOM2DTM.java:935) at org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.getNodeData(DOM2DTM.java:928) at org.apache.xml.dtm.ref.dom2dtm.DOM2DTM.getStringValue(DOM2DTM.java:827) at org.apache.xpath.objects.XNodeSet.getStringFromNode(XNodeSet.java:217) at org.apache.xpath.objects.XNodeSet.xstr(XNodeSet.java:256) at org.apache.xpath.functions.FunctionDef1Arg.getArg0AsString(FunctionDef1Arg.java:97) at org.apache.xpath.functions.FuncStringLength.execute(FuncStringLength.java:45) at org.apache.xpath.Expression.execute(Expression.java:155) at org.apache.xpath.operations.Operation.execute(Operation.java:110) at org.apache.xpath.Expression.bool(Expression.java:186) at org.apache.xpath.XPath.bool(XPath.java:412) at org.apache.xalan.templates.ElemChoose.execute(ElemChoose.java:126) at org.apache.xalan.transformer.TransformerImpl.executeChildTemplates(TransformerImpl.java:2400) at org.apache.xalan.templates.ElemTemplate.execute(ElemTemplate.java:394) at org.apache.xalan.templates.ElemCallTemplate.execute(ElemCallTemplate.java:248)


Solution

  • I am confused by the need for two templates here, as you could combine the logic into one, like so

    <xsl:template name="zero_width_space_1">
        <xsl:param name="data"/>
        <xsl:param name="counter" select="0"/>
        <xsl:if test="$counter &lt;= (string-length($data))">
            <xsl:value-of select='concat(substring($data,$counter,1),"&#8203;")'/>
            <xsl:call-template name="zero_width_space_1">
                <xsl:with-param name="data" select="$data"/>
                <xsl:with-param name="counter" select="$counter+1"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    

    Also note, your function is not quite right, it should look like this...

    <func:function name="func:callTemplate">
        <xsl:param name="string1"/>
        <xsl:variable name="result">
            <xsl:call-template name="zero_width_space_1">
                <xsl:with-param name="data" select="$string1"/>
            </xsl:call-template>
        </xsl:variable>
        <func:result select="$result" />
    </func:function>    
    

    I am not sure if that will solve your stack overflow issue. However, if the purpose is to insert a &#8203; before each character, then you could make use of the tokenize function available in exslt

    <func:function name="func:callTemplate">
        <xsl:param name="string1"/>
        <xsl:variable name="result">
            <xsl:for-each select="str:tokenize($string1, '')">
                <xsl:value-of select="concat('&#8203;', .)" />
            </xsl:for-each>
            <xsl:text>&#8203;</xsl:text>
        </xsl:variable>
        <func:result select="$result" />
    </func:function>