xsl-list
[Top] [All Lists]

Re: [xsl] Processing milestoned XML leads to many preceding:: calls and horrible performance

2012-02-21 04:02:42
   <xsl:template name="genRef">
       <xsl:variable name="refKniha" select="//kniha[1]/@jmeno"/>
       <xsl:variable name="refKapitola" select="preceding::kap[1]/@n"/>
       <xsl:value-of select="concat($refKniha,'.',$refKapitola,'.')"/>
   </xsl:template>

Did you really want "//kniha[1]" or "(//Kniha)[1]" ?   It's a common
gotcha...  The former can select multiple elements, not just one.

Also, with named templates I always think you should pass in the
$context as a parameter rather than rely on the implicit one as then
you can give it a sequence type helping the maintain know what it
expected (and it helps debugging).  In this case, as you returning an
atomic you should really use a function.

       <xsl:element name="verse">

You can just write <verse> directly here, no need for xsl:element.

           <xsl:variable name="prevVerseID">
               <xsl:value-of select="./preceding::vers[1]/@n" />
           </xsl:variable>

You should use the select attribute here, eg:

<xsl:variable name="prevVerseID" select="preceding::vers[1]/@n" />

           <xsl:attribute name="eID">
               <xsl:value-of select="concat($rBase,$prevVerseID)" />
           </xsl:attribute>

That could be an "attribute value template" (then there's no need for
the above):

<verse eID="{concat($rBase, preceding::vers[1]/@n}"/>

...however you could probably use a tunnelled parameter to pass the @n
forwards rather than look back for it.

       <xsl:variable name="refBase">
           <xsl:call-template name="genRef" />
       </xsl:variable>

Again, this is a bit nasty and should replaced with a function call.


       <xsl:variable name="curPos"

select="count(./preceding::kap[1]/following::*[not(count(preceding-sibling::vers|current())
= count(preceding-sibling::vers))])" />

You say that's to check if it's the first <vers> in the chapter... a
good way to do that is at the parent node select the <vers> child you
want to treat as the first, and then pass it is as a parameter.  Then
in the <vers> template, you can say:

select=". is $first-vers"

to see if the <vers> you are processing is the one you want to treat
as the first.  This makes it clear what you are trying to do, and
avoids looking back up the tree.

       <xsl:if test="not($curPos=1)">
           <xsl:call-template name="endVerse">
               <xsl:with-param name="rBase">
                   <xsl:value-of select="$refBase" />
               </xsl:with-param>
           </xsl:call-template>
       </xsl:if>

The last thing to say is: avoid named templates.  You very rarely need
them, typically only when you want to output boilerplate markup, and
even then moded templates are often better.


Any ideas? Would some other XSLT processors other than xsltproc (libxml
20706, libxslt 10126 and libexslt 815) I am using be able to optimize this
somehow?

ahhh... most of the above only applies for xslt 2.0.  These days it's
helpful to mention the version you are using near the top :)



-- 
Andrew Welch
http://andrewjwelch.com

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--


<Prev in Thread] Current Thread [Next in Thread>