RE: [xsl] Omnimark vs. XSL (Saxon) Challenge

Subject: RE: [xsl] Omnimark vs. XSL (Saxon) Challenge
From: "Andrew Welch" <AWelch@xxxxxxxxxxxxxxx>
Date: Thu, 18 Mar 2004 10:20:39 -0000
Heres a potential solution...

It works by first creating a variable with the colspans normalized,
which is easy enough.  It then normalizes the rowspans by iterating over
the variable a row at time, using two nodesets.  One is the current row,
one is the previous row ($rowBlock). Where the previous row has a <td>
with a rowspan > 1, it copies it to a result variable with the rowspan
decremented.  Where the previous row just has a normal <td>, it copies
the correct <td> from the current row to the result variable.  It
maintains a pair of pointers to ensure the correct <td>'s are being
compared.  The 'result variable' is then used as the previous row in the
next iteration of the <tr> processing.  This is based on the fact that
the first row of the table is always complete - it contains the correct
number of columns.

Currently it gives the result:

      Col 1= 40%
      Col 2= 30%
      Col 3= 20%
      Col 4= 

Hopefully you can get what you need from that (and that it's correct!)

Performance wise, it produced:

 4 rows           256 rows
 2.125            106.752
 2.135            116.569
 2.115            106.027

(using Xselerator with msxml4, pentium 4-M 2ghz 512mb)

This is linear, so hopefully you can use it. Please post back any
comments/improvements.

cheers
andrew

(apologies for the lack of comments)

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:exsl="http://exslt.org/common";
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
extension-element-prefixes="exsl msxsl">

<xsl:variable name="table_with_no_colspans-rtf">
    <xsl:apply-templates mode="colspan"/>
</xsl:variable>
<xsl:variable name="table_with_no_colspans"
select="exsl:node-set($table_with_no_colspans-rtf)"/>

<xsl:variable name="table_with_no_rowspan-rtf">
  <xsl:for-each select="$table_with_no_colspans">
    <xsl:apply-templates mode="rowspan"/>
  </xsl:for-each>
</xsl:variable>
<xsl:variable name="table_with_no_rowspan"
select="exsl:node-set($table_with_no_rowspan-rtf)"/>

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

<xsl:template match="@*|*" mode="colspan">
  <xsl:copy>
    <xsl:apply-templates select="@*|*" mode="colspan"/>
  </xsl:copy>
</xsl:template>


<xsl:template match="td" mode="colspan">
  <xsl:choose>
    <xsl:when test="@colspan">
      <td>
        <xsl:copy-of select="@rowspan"/>
        <xsl:value-of select="."/>
      </td>
      <xsl:call-template name="give_me_tds">
        <xsl:with-param name="number-of-tds" select="number(@colspan) -
1"/>
        <xsl:with-param name="rowspan">
          <xsl:choose>
            <xsl:when test="@rowspan > 1">
              <xsl:value-of select="@rowspan"/>
            </xsl:when>
            <xsl:otherwise>1</xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <td>
        <xsl:choose>
          <xsl:when test="@rowspan">
            <xsl:copy-of select="@rowspan"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:copy-of select="@width"/>
          </xsl:otherwise>
        </xsl:choose>
        <xsl:value-of select="."/>
      </td>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="give_me_tds">
  <xsl:param name="number-of-tds" select="'1'"/>
  <xsl:param name="rowspan"/>
  <xsl:choose>
    <xsl:when test="$number-of-tds > 1">
      <td rowspan="{$rowspan}">
        <xsl:value-of select="."/>
      </td>
      <xsl:call-template name="give_me_tds">
        <xsl:with-param name="number-of-tds" select="$number-of-tds -
1"/>
        <xsl:with-param name="rowspan" select="$rowspan"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <td rowspan="{$rowspan}">
        <xsl:value-of select="."/>
      </td>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="/">
  <div>
    <XXXXX><xsl:copy-of select="$table_with_no_colspans"/></XXXXX>
    <ZZZZZ><xsl:copy-of select="$table_with_no_rowspan"/></ZZZZZ>
    
    <xsl:for-each select="$table_with_no_rowspan/*/tr[1]/td">
      <xsl:variable name="pos" select="position()"/>
      <xsl:text/>Col <xsl:value-of select="$pos"/>= <xsl:text/>
      <xsl:value-of
select="$table_with_no_rowspan//tr/td[$pos]/@width"/>
      <xsl:text>
      </xsl:text>
    </xsl:for-each>
  </div>
</xsl:template>

<xsl:template match="td" mode="final">
  <xsl:copy>
    <xsl:copy-of select="@width"/>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>

<xsl:template match="table" mode="rowspan">
  <div>
    <xsl:copy-of select="tr[1]"/>

    <xsl:apply-templates select="tr[2]" mode="rowspan">
      <xsl:with-param name="rowBlock" select="tr[1]"/>
    </xsl:apply-templates>
  </div>
</xsl:template>


<xsl:template match="tr" mode="rowspan">

  <xsl:param name="rowBlock" select="/.."/>

  <xsl:variable name="current" select="."/>

  <xsl:variable name="rowBlockVar">
    <xsl:call-template name="makeRow">
      <xsl:with-param name="rowBlock" select="$rowBlock"/>
      <xsl:with-param name="current" select="$current"/>
    </xsl:call-template>
  </xsl:variable>

  <tr>
    <xsl:copy-of select="$rowBlockVar"/>
  </tr>
  
  <xsl:apply-templates select="following-sibling::tr[1]" mode="rowspan">
    <xsl:with-param name="rowBlock"
select="exsl:node-set($rowBlockVar)"/>
  </xsl:apply-templates>

</xsl:template>

<xsl:template name="makeRow">
  <xsl:param name="rowBlock"/>
  <xsl:param name="current"/>
  <xsl:param name="rowBlockPointer" select="1"/>
  <xsl:param name="currentPointer" select="1"/>

  <xsl:variable name="rbRowspan">
    <xsl:for-each select="$rowBlock">
      <xsl:choose>
        <xsl:when test="td[$rowBlockPointer]/@rowspan">
          <xsl:value-of select="td[$rowBlockPointer]/@rowspan"/>
        </xsl:when>
        <xsl:otherwise>1</xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </xsl:variable>

  <xsl:if test="$rowBlock/td[$rowBlockPointer]">

  <xsl:choose>
    <xsl:when test="$rbRowspan > 1 or
not($current/td[$currentPointer])">
      <td rowspan="{$rbRowspan - 1}">
        <xsl:value-of select="$rowBlock/td[$rowBlockPointer]"/>
      </td>
      <xsl:call-template name="makeRow">
        <xsl:with-param name="rowBlock" select="$rowBlock"/>
        <xsl:with-param name="current" select="$current"/>
        <xsl:with-param name="rowBlockPointer" select="$rowBlockPointer
+ 1"/>
        <xsl:with-param name="currentPointer" select="$currentPointer"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:for-each select="$current">
      <xsl:copy-of select="td[$currentPointer]"/>
        <xsl:call-template name="makeRow">
          <xsl:with-param name="rowBlock" select="$rowBlock"/>
          <xsl:with-param name="current" select="$current"/>
          <xsl:with-param name="rowBlockPointer"
select="$rowBlockPointer + 1"/>
          <xsl:with-param name="currentPointer" select="$currentPointer
+ 1"/>
        </xsl:call-template>
      </xsl:for-each>
    </xsl:otherwise>
  </xsl:choose>

  </xsl:if>

</xsl:template>

</xsl:stylesheet>

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread