Doing file path manipulations in XSLT
Asked Answered
A

1

7

I'd like my generated output file to contain file paths that point to a path relative to the stylesheet. The location of the stylesheet can change and I don't want to use a parameter for the stylesheet. My solution for this is to get the full stylesheet URI:

<xsl:variable name="stylesheetURI" select="document-uri(document(''))" />

Now I only need to cut off the filename from $stylesheetURI. This has inspired me to write XSLT 2.0 clones of the PHP functions basename and dirname:

<xsl:function name="de:basename">
    <xsl:param name="file"></xsl:param>
    <xsl:sequence select="tokenize($file, '/')[last()]" />
</xsl:function>

<xsl:function name="de:dirname">
    <xsl:param name="file"></xsl:param>
    <xsl:sequence 
        select="string-join(tokenize($file, '/')[position() != last()], '/')" />
</xsl:function>

Now I can do something like this in my template:

<img src="{concat(de:dirname($stylesheetURI),'/img/myimage,png')}" />

My question is: Are there better/faster ways to accomplish this with native XSLT 2.0?

Anybody answered 25/6, 2010 at 9:29 Comment(1)
I enjoyed working on this question (+1). See my answer for what seem to be 25% faster. :)Antonetta
A
8

I tested (not too extensively) these functions and they seem to perform 25% faster than the provided. Of course, the results depend on the string length and the number of qualifiers:

  <xsl:function name="de:basename" as="xs:string">
    <xsl:param name="pfile" as="xs:string"/>
    <xsl:sequence select=
     "de:reverseStr(substring-before(de:reverseStr($pfile), '/'))
     " />
  </xsl:function>

  <xsl:function name="de:dirname" as="xs:string">
    <xsl:param name="pfile" as="xs:string"/>
    <xsl:sequence select=
     "de:reverseStr(substring-after(de:reverseStr($pfile), '/'))
     " />
  </xsl:function>

  <xsl:function name="de:reverseStr" as="xs:string">
    <xsl:param name="pStr" as="xs:string"/>

    <xsl:sequence select=
    "codepoints-to-string(reverse(string-to-codepoints($pStr)))"/>
  </xsl:function>
Antonetta answered 25/6, 2010 at 13:18 Comment(4)
Wow! Could it be this because sequence implementation (with tokenize and string-join) have more porform costs than string manipulation? But, doesn't string-to-codeponits also return a sequence?Kagera
@Alejandro: tokenize() does a lot of work -- scanning char by char and placing every token in a separate item. Wherever substring-before() or substring-after() only scan up to the first occurence.Antonetta
This works great for Unix-style pathnames, but our XSLT must also run on Windows (with their curs’ed '\' path separator). Am going to have to think about how either to generalize this function, or detect the OS and change the path separator accordingly.Martin
@CaryMillsap, just add another parameter named, say, pSeparator, and pass '/' in the case of Unix and '\' in the case of windows. In XPath 3.0 there are standard functions for getting environment variables and it would be possible to get the OS and separator in this way.Antonetta

© 2022 - 2024 — McMap. All rights reserved.