Re: [xsl] Nodes get new IDs in function?

Subject: Re: [xsl] Nodes get new IDs in function?
From: "Imsieke, Gerrit, le-tex" <gerrit.imsieke@xxxxxxxxx>
Date: Sat, 10 Apr 2010 17:15:18 +0200
On 09.04.2010 11:16, Martynas Jusevicius wrote:
Thanks all for pointing out the xsl:copy-of, now it works like a charm.

As for the grouping approach, I'm considering it, but I can't see why
it's so much better? Performance wise? But it's using recursion as
well?

If the task is to extract only the ToC, it isn't much better. If the task was to hierarchize the whole document, for-each-group may prove less cumbersome than collecting nodes between headings.


Maybe we can agree that for this problem, it is a matter of personal programming style.

I think after I introduced parent-heading() function, my solution
became quite elegant. It's also convenient to have this function in
other situations.

Of course.



Another question is, what is the desired output. You start at level 1, what if there are no h1 elements?

Just change h1 to h5 or whatever in the input data and see the results.


Also, in your solution<li class="level4"><p>Attribution</p></li>  gets
nested in an additional<ul>, which I think is unnecessary. In my
version, it gets flattened to the same level as<li class="level3">.

I'm using the same approach not only for<ul>  list in XHTML, but NCX
table of contents as well. In Adobe Digital Editions, all top-level
headings are collapsed by default (children not shown until clicked),
so having empty nesting elements is not really a good idea.

You are right, your nesting model is better suited for NCX generation.


But of course there's a for-each-group solution to this nesting problem ("if your only tool is a hammer, every problem looks like a nail" -- for-each-group being my hammer):

==========8<-------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  xmlns:xs="http://www.w3.org/2001/XMLSchema";
  xmlns:my="URN:my"
  version="2.0"
  exclude-result-prefixes="my xs"
  >

<xsl:output method="xml" indent="yes" />

  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="body">
    <xsl:copy>
      <xsl:sequence select="my:hierarchize-toc(*)" />
    </xsl:copy>
  </xsl:template>

<xsl:function name="my:hierarchize-toc" as="element(*)*">
<xsl:param name="nodes" as="element(*)*" />
<ul>
<xsl:for-each-group select="$nodes"
group-starting-with="*[my:isHeading(.)][
my:hlevel(.) le min(
(
for $ph in (
preceding-sibling::*
intersect $nodes[my:isHeading(.)]
)
return my:hlevel($ph),
6
)
)
]">
<xsl:if test="my:isHeading(.)">
<li class="level{my:hlevel(.)}">
<p><xsl:copy-of select="node()" /></p>
<xsl:variable name="subtoc"
select="my:hierarchize-toc(current-group()[position() gt 1])" />
<xsl:if test="$subtoc/*">
<xsl:sequence select="$subtoc" />
</xsl:if>
</li>
</xsl:if>
</xsl:for-each-group>
</ul>
</xsl:function>


  <xsl:function name="my:isHeading" as="xs:boolean">
    <xsl:param name="elt" as="element(*)" />
    <xsl:value-of select="matches(local-name($elt), '^h\d$')" />
  </xsl:function>

<xsl:function name="my:hlevel" as="xs:double">
<xsl:param name="elt" as="element(*)" />
<xsl:value-of select="number(replace(local-name($elt), '^h(\d)$', '$1'))" />
</xsl:function>


</xsl:stylesheet>
==========8<-------------------------------------------

-Gerrit

P.S.: I seem to use at least one other tool on a regular basis than just for-each-group: regex functions. You use substring-after which is absolutely adequate there. replace may be considered as overkill. Also a matter of personal preference.

P.P.S.: ToC creation and XHTML splitting will get tougher if you permit <div>s in the XHTML where
- single headings,
- headings together with a section's introductory content, or
- whole sections
are present within these possibly nested divs.


Current Thread