[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: Nested xsl:iterate with innermost xsl:on-completio

Subject: Re: Nested xsl:iterate with innermost xsl:on-completion?
From: Michael Kay <mike@xxxxxxxxxxxx>
Date: Fri, 14 Feb 2014 08:47:15 +0000
Re:  Nested xsl:iterate with innermost xsl:on-completio
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@xxxxxxxxxx> 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@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

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.