Subject: [xsl] Nested xsl:iterate with innermost xsl:on-completion? From: "Tony Graham" <tgraham@xxxxxxxxxx> Date: Thu, 13 Feb 2014 18:55:43 -0000 (GMT) |
To try out xsl:iterate, I'm trying to process a HTML-like table to HTML but with empty <td> added where there's a 'gap' in the original table. E.g., in the following three-column table, there's a 'gap' at the end of the head row and at the end of the second body row: ---- <table border="1"> <colgroup> <col align="left" span="1"/> <col align="left" span="1"/> <col align="left" span="1"/> </colgroup> <thead> <tr> <td rowspan="1" colspan="1">Head</td> <td rowspan="1" colspan="1"></td> </tr> </thead> <tbody> <tr> <td rowspan="2" colspan="1">1.1, 2.1</td> <td rowspan="1" colspan="1">1.2</td> <td rowspan="1" colspan="1">1.3</td> </tr> <tr> <td rowspan="1" colspan="1">2.2</td> </tr> </tbody> </table> ---- In the spirit of 'XSLT 3.0 even if it kills me', I'm using a map to keep track of cells spanning between rows (and handling column spans is an exercise for another day). For <thead> and <tbody>, the stylesheet below uses xsl:iterate to iterate over the <tr> in the <thead> or <tbody>, and that xsl:iterate contains another xsl:iterate for iterating over the table cells. The map for keeping track of cells spanning table rows is updated okay, but the only way I've found to use the updated map in a subsequent row is to return the map as the result of an xsl:on-completion in the inner xsl:iterate. That wouldn't be so bad, but currently it means putting the entire result of processing the row into a $content variable just so everything except the last item can be copied to the result and the last item -- the result of the xsl:on-completion -- can be used in the xsl:next-iteration of the outer xsl:iterate. Is there a better way to do this rather than buffering the result of an xsl:iterate just to also get a variable's value? Regards, Tony Graham tgraham@xxxxxxxxxx Consultant http://www.mentea.net Chair, Print and Page Layout Community Group @ W3C XML Guild member -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Mentea XML, XSL-FO and XSLT consulting, training and programming ---- <?xml version="1.0"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:x3tb="https://github.com/MenteaXML/xslt3testbed" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xlink map mml x3tb xs"> <xsl:output method="html"/> <xsl:template match="/"> <html> <head></head> <body> <xsl:apply-templates /> </body> </html> </xsl:template> <xsl:template match="table"> <xsl:variable name="cols" as="map(*)*"> <xsl:for-each select="col | colgroup/col"> <xsl:sequence select="map:entry(position(), .)" /> </xsl:for-each> </xsl:variable> <xsl:variable name="col-map" as="map(xs:integer, element(col))" select="map:new($cols)" /> <xsl:copy> <xsl:apply-templates select="@*" mode="table-copy"/> <xsl:apply-templates> <xsl:with-param name="col-map" select="$col-map" as="map(xs:integer, element(col))" /> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="thead | tbody | tfoot"> <xsl:param name="col-map" as="map(xs:integer, element(col))" /> <xsl:variable name="cell-map" select="map:new(for $i in 1 to count(map:keys($col-map)) return map:entry($i, 0))" as="map(xs:integer, xs:integer)" /> <xsl:copy> <xsl:apply-templates select="@*" mode="table-copy"/> <xsl:iterate select="tr"> <xsl:param name="cell-map" select="$cell-map" as="map(xs:integer, xs:integer)" /> <xsl:message select="('row', count(map:keys($cell-map)))"/> <xsl:variable name="content" as="item()+"> <xsl:iterate select="1 to count(map:keys($cell-map))"> <xsl:param name="colnum" select="1" as="xs:integer" /> <xsl:param name="cell-map" select="$cell-map" as="map(xs:integer, xs:integer)" /> <xsl:param name="cells" select="*" as="element()*" /> <xsl:message select="map:get($cell-map, .)"/> <xsl:choose> <xsl:when test="map:get($cell-map,.) > 0"> <xsl:message select="'rowspanned'"/> <xsl:next-iteration> <xsl:with-param name="colnum" select="$colnum + 1" as="xs:integer" /> <xsl:with-param name="cell-map" select="map:new(($cell-map, map:entry(., map:get($cell-map,.) - 1)))" as="map(xs:integer, xs:integer)" /> </xsl:next-iteration> </xsl:when> <xsl:when test="empty($cells)"> <xsl:message select="'no cell'"/> <td/> <xsl:next-iteration> <xsl:with-param name="colnum" select="$colnum + 1"/> </xsl:next-iteration> </xsl:when> <xsl:otherwise> <xsl:message select="concat('cell: ''', $cells[1]), '''; rowspan: ', string($cells[1]/@rowspan)"/> <xsl:apply-templates select="$cells[1]" /> <xsl:next-iteration> <xsl:with-param name="colnum" select="$colnum + 1" as="xs:integer" /> <xsl:with-param name="cell-map" select="map:new(($cell-map, map:entry(., xs:integer(($cells[1]/@rowspan, 1)[1]) - 1)))"/> <xsl:with-param name="cells" select="$cells[position() > 1]"/> </xsl:next-iteration> </xsl:otherwise> </xsl:choose> <xsl:on-completion> <xsl:sequence select="$cell-map" /> </xsl:on-completion> </xsl:iterate> </xsl:variable> <xsl:copy> <xsl:apply-templates select="@*" mode="table-copy"/> <!--<xsl:call-template name="named-anchor"/>--> <xsl:sequence select="$content[position() != last()]" /> </xsl:copy> <xsl:next-iteration> <xsl:with-param name="cell-map" select="$content[last()]" /> </xsl:next-iteration> </xsl:iterate> </xsl:copy> </xsl:template> <xsl:template match="col | colgroup | th | td"> <xsl:copy> <xsl:apply-templates select="@*" mode="table-copy"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> <xsl:template match="@*" mode="table-copy"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet> ----
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] things about grouping, Ihe Onwuka | Thread | Re: [xsl] Nested xsl:iterate with i, Michael Kay |
Re: [xsl] Ignoring ambiguous matche, Dimitre Novatchev | Date | Re: [xsl] Verifying large XSL trans, Greg Hunt |
Month |