If I've understood this correctly, it's a classic case where you want to do
some processing that returns a "primary result" - the result of
transforming/formatting the table cells, and also a "secondary result" -
information about the row spans.
Returning the two results as a combined value (perhaps a map, or as you are
doing, a sequence) is one way. Another way is to split the two computations,
processing the same input twice in two different ways to compute the two
results. As you point out, neither is very satisfactory; doing two passes is
likely to involve duplication of effort, while the single-pass, composite
result approach is likely to involve storing the whole composite result in
memory.
I think I would be inclined here to compute the cell map in a first pass over
the input, and then pass this as a parameter to the second pass which generates
the "primary" output.
An alternative you could consider would be to compute the cell map in an
accumulator.
Michael Kay
Saxonica
On 13 Feb 2014, at 18:55, Tony Graham <tgraham(_at_)mentea(_dot_)net> wrote:
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(_at_)mentea(_dot_)net
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>
----
--~------------------------------------------------------------------
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>
--~--
--~------------------------------------------------------------------
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>
--~--