0

I tried to implement a "ternary operator" extension function for use in my stylesheet by using EXSLT's func:function element. For compatibility reasons, I have to use XSLT 1.0. I came up with this:

<func:function name="myext:ternary">
  <xsl:param name="expr" />
  <xsl:param name="ifTrue" />
  <xsl:param name="ifFalse" />

  <func:result>
    <xsl:choose>
      <xsl:when test="boolean($expr)"><xsl:value-of select="$ifTrue"/></xsl:when>
      <xsl:otherwise><xsl:value-of select="$ifFalse" /></xsl:otherwise>
    </xsl:choose>
  </func:result>
</func:function>

and it works fine wherever I use it. However, I also tried to implement a substring-after-last function (from here). This code works fine:

<func:function name="myext:substring-after-last">
  <xsl:param name="string" select="''"/>
  <xsl:param name="delimiter" select="$d" />

  <func:result>
    <xsl:choose>
      <xsl:when test="contains($string, $delimiter)">
        <xsl:value-of select="myext:substring-after-last(substring-after($string, $delimiter), $delimiter)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$string"/>
      </xsl:otherwise>
    </xsl:choose>
  </func:result>
</func:function>

This way it also works:

[…]
  <func:result>
    <xsl:if test="not(contains($string, $delimiter))">
      <xsl:value-of select="$string"/>
    </xsl:if>
    <xsl:if test="not($string='')">
      <xsl:value-of select="myext:substring-after-last(substring-after($string, $delimiter), $delimiter)"/>
    </xsl:if>
  </func:result>
</func:function>

… but when I try an implementation using the "ternary" function, it does not work;

This doesn't work:

<func:result>
  <xsl:value-of select="myext:ternary(
    contains($string, $delimiter) and not($string = ''),
    myext:substring-after-last(substring-after($string, $delimiter), $delimiter),
    $string
  )"/>
</func:result>

This method results in my stylesheet throwing this error at me:

Error at xsl:apply-templates on line 49 of file:/[my_main_file].xsl:
  Too many nested apply-templates calls
Transformation failed: Run-time errors were reported

I've tried to increase Java's stack size by using the -Xss16m switch, but that just results in Saxon throwing an OutOfMemoryError (Java heap space). The data sets that I send to this function are rather small, so I don't understand where the overflow happens and why it should even be necessary for me to try to increase the stack size.

What am I doing wrong?

Community
  • 1
  • 1
Slampisko
  • 99
  • 11
  • If you use Saxon and Java can't you simply use Saxon 9 and XSLT 2.0 which has a user defined functions built-in as a feature? Why do you need to use EXSLT? – Martin Honnen Jul 31 '13 at 09:38
  • And with XSLT 2.0 all you want is `tokenize($string, $delimiter)[last()]`. – Martin Honnen Jul 31 '13 at 09:43
  • Sorry for not specifying the version, I have to use XSLT 1.0 for corporate compatibility reasons. I have edited the post. – Slampisko Jul 31 '13 at 10:15

1 Answers1

0

I tried to implement a "ternary operator" extension function

Use a union of a position predicate with a negation predicate variable check as an else statement:

//var[1] | //var[not //var]

Use a concatenation of mutually exclusive expressions as a ternary idiom:

concat("(",substring(translate(., "()-", ""), 1, 3), ")", substring(translate(., "()-", ""), 4, 3), "-", substring(translate(., "()-", ""), 7, 4)))

in XPath number(true()) is 1, while number(false()) is 0 and when second argument of substring() function is greater than actual length of the string, empty string is returned. Hence substring($s1, number(not($condition))*string-length($s1)+1) returns $s1 if $condition is true and empty string otherwise. Concatenating two such expressions in mutually exclusive way gives us conditional strings expression.

References

Community
  • 1
  • 1
Paul Sweatte
  • 24,148
  • 7
  • 127
  • 265