Re: [xsl] Slow XSLT

Subject: Re: [xsl] Slow XSLT
From: Cleyton Jordan <cleytonjordan@xxxxxxxxxxx>
Date: Fri, 14 Mar 2008 21:42:48 +0000 (GMT)
Hi Manfred,

Thanks again for your assistance. It is really
appreciated.

I will try your sample code and let you know what
happens.

Also, as I mentined before, I am trying to build a
GRID and will send to you a copy of my latest XSLT so
that you can see what I have achieved so far. Slowly I
am getting there :-)

THE GRID

Basically, I use four <DIV> and <TABLE> tags. I have
the Top Left DIV, Top Right DIV, Bottom Left DIV and
Bottom Rght DIV.

Because I use Javascript to align my GRID, I have to
apply templates in a certain order i.e:

1- build the bottom right section first (grdBody)
2- next build the top right section (column headings)
3- Build the bottom left section (row headings)
4- Finaly build the top left section which is just an
empty div with the right hight and width to align with
the rest of the grid.

Please note that I still have to change my XSLT code
to use the sample code you showed me (call template
and tail recursion). I am still using my old key way I
mentioned to you.

I am struggling a bit with some of the suggestions I
have received specially the sample code provided by
David Carlisle. He showed me how to use a technique
that Michael calls vanilla push-processing. 

However, since I have to sort the xml based on some
parameters passed to the stylesheet, I am not sure
where to add the <xsl:sort> command with
<apply-templates>. 

I have to sort 3 sections of the xml. This is how I
used to do:

<xsl:sort select
="Cell[number($sortCell)]/descendant::Msr[number($sortCol)]/@val"
data-type="{$dataType}" order ="{$sortOrder}" />

I have to sort all the rows except for the last one -
the Total row of course :-). That is the reason for
the when test:

OLD REPORT TEMPLATE WITH SORTING

<xsl:template match="Report" >
<!-- grdBdy -->
      <div id="{$id}grdBdy">
        <table id="tblTop" cellpadding="0">          
          <tbody>
            <xsl:choose>
              <xsl:when  test="number($sortCol) != 0">
                <xsl:apply-templates
select="/Reports/Report/Rows//Row" mode="data">   
                  <xsl:sort select
="Cell[number($sortCell)]/descendant::Msr[number($sortCol)]/@val"
data-type="{$dataType}" order ="{$sortOrder}" />
                </xsl:apply-templates>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates
select="/Reports/Report/Rows//Row" mode="data" />     
  
              </xsl:otherwise>
            </xsl:choose>
          </tbody>
        </table>
      </div>
</xsl:template>

I also have to add a sort command to sort the Row
Headings:

OLD REPORT TEMPLATE WITH ROW HEADING SORTING

<xsl:template match="Report" >
<!-- grdLeft -->   
      <div id="{$id}grdLft" style="position: absolute;
top: 12px; left: 0px; width: 150px; height:
{$height}px">
        <table width="100%" class="grdMain" border="0"
cellspacing="1" cellpadding="0">
          <tbody>           
            <xsl:choose>
              <xsl:when  test="number($sortCol) != 0">
                <xsl:apply-templates
select="Rows//Row" mode="heading">
                  <xsl:sort select
="Cell[number($sortCell)]/descendant::Msr[number($sortCol)]/@val"
data-type="{$dataType}" order ="{$sortOrder}" />
                </xsl:apply-templates>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates
select="Rows//Row" mode="heading" />
              </xsl:otherwise>
            </xsl:choose>
          </tbody>
        </table>
      </div>
</xsl:template>

Please note that I used to be able to control all
these sections using the MODE ATTRIBUTE

<xsl:apply-templates select="Rows//Row"
mode="heading">

However, with apply-templates I might not have this
control. What do you think?

The last place I have to sort is in the Cell template.
Please note that I have removed this sort command from
my latest XSLT because I am not sure how to do that
now.  

<xsl:template match="Cell">    
    <xsl:choose>
      <xsl:when test="Msr">
        <xsl:for-each select="Msr">
          <xsl:sort select="@idx" order="ascending"
data-type="number"/>
          <xsl:choose>
.....
....
</xsl:template>
 
I would appreciate your suggestion about the sorting
issues I mentioned above and also any other
improvement you may find possible.

Just one last note, I still have to figure out the
best way to deal with the ROW HEADINGS. Still using
old code like <xsl:if
test="preceding-sibling::Row=false() or
number($sortCol) != 0">
        <xsl:apply-templates select="parent::RowGrp"
mode="heading" />. 

How difficult do you think it would be to change my
approach of using 4 DIVs and 4 Tables to build the
grid and instead creating just one table? 

However, I would have to make sure I get my rowspan
and colspan right for the first TD in the first <tr>.

Cheers

C


XSLT
____

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xsl:output method="html" version="1.0"
encoding="iso-8859-1" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!--<xsl:decimal-format name="fd1"
decimal-separator="." grouping-separator=","
NaN=" \"/>-->
  <xsl:param name="axisHeads" select="'false'"/>
  <xsl:param name="sortCol" select="'0'"/>
  <xsl:param name="sortCell" select="'0'"/>
  <xsl:param name="dataType" select="'number'"/>
  <xsl:param name="sortOrder" select="'descending'"/>
  <xsl:param name="ltCurFormat"
select="'$##,###.00'"/>
  <xsl:param name="ltNumFormat"
select="'###,.00'"/>
  <xsl:param name="heading"/>
  <xsl:param name="height" select="'500'"/>
  <xsl:param name="width" select="'880'"/>
  <xsl:param name="id" select="'audit'"/>

  <xsl:variable name="nrmr"
select="count(/Report/Measures/Measure)"/>
  <xsl:variable name="msrs"
select="/Report/Measures/Measure"/>
  <xsl:variable name="colSet"
select="/Report/Columns/ColGrp/descendant::Col"/>

  <xsl:key name="c" match="ColGrp"
use="count(ancestor::ColGrp)"/>

  <xsl:template match="Report">

    <!-- outer div to contain all four grid elements
-->
    <div id="{$id}dvGrid" class="grdVP"
style="position: relative; height: {$height}px; width:
{$width}px; overflow: hidden; clip: rect(0px,
{$width}px, {$height}px, 0px);">

      <!-- grdBdy -->
      <div id="{$id}grdBdy" style="position: relative;
width: {$width}px; height: {$height}px; overflow:auto"
onscroll="scrollFullGrid(this)">
        <table class="grdMain" id="tblTop" border="0"
cellspacing="1" cellpadding="0">
          <tbody>
            <xsl:choose>
              <xsl:when  test="number($sortCol) != 0">
                <xsl:apply-templates>
                  <xsl:sort select
="Cell[number($sortCell)]/descendant::Msr[number($sortCol)]/@val"
data-type="{$dataType}" order ="{$sortOrder}" />
                </xsl:apply-templates>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates/>
              </xsl:otherwise>
            </xsl:choose>
          </tbody>
        </table>
      </div>

      <div id="{$id}grdTop" style="position: absolute;
top: 0px; left: 0px; width: {$width}px; height: 12px">
        <table class="grdTop" border="0"
cellspacing="1" cellpadding="0">
          <tbody>
            <xsl:apply-templates select="Columns"
mode="Column"/>
          </tbody>
        </table>
      </div>

      <!-- grdLeft -->
      <div id="{$id}grdLft" style="position: absolute;
top: 12px; left: 0px; width: 150px; height:
{$height}px">
        <table width="100%" class="grdMain" border="0"
cellspacing="1" cellpadding="0">
          <tbody>
            <xsl:choose>
              <xsl:when  test="number($sortCol) != 0">
                <xsl:apply-templates
select="Rows//Row" mode="heading">
                  <xsl:sort select
="Cell[number($sortCell)]/descendant::Msr[number($sortCol)]/@val"
data-type="{$dataType}" order ="{$sortOrder}" />
                </xsl:apply-templates>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates
select="Rows//Row" mode="heading" />
              </xsl:otherwise>
            </xsl:choose>
          </tbody>
        </table>
      </div>

      <!-- grdTL the filler space - formerly the td
with colspan and rowspan -->

      <div id="{$id}grdTL" class="grdTL"
style="position: absolute; width:150px;height:12px">
        <div></div>
      </div>
    
    </div>

  </xsl:template>


  <!--== ROW DATA   ==-->

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

  <xsl:template match="Msr">
    <xsl:variable name="numberVal" select="@val"/>
    <xsl:choose>
      <!--<xsl:when test="string(number(@val))
='NaN'">-->
      <xsl:when test="not(number($numberVal))">
        <td align="left" style="overflow:none">
          <nobr>
            <div style="width:80px;overflow:none">
              <xsl:value-of select="$numberVal"/>
            </div>
          </nobr>
        </td>
      </xsl:when>
      <xsl:otherwise>
        <td align="right" style="overflow:none">
          <nobr>
            <div style="width:80px;overflow:none">
              <!--<xsl:variable name="numberVal"
select="@val"/>-->
              <xsl:variable name="style"
select="@class"/>
              <xsl:if test="$numberVal != ''">
                <xsl:choose>
                  <xsl:when test="$style='num1'">
                    <xsl:value-of
select="format-number($numberVal,'###,')"/>
                  </xsl:when>
                  <xsl:when test="$style='cur1'">
                    <xsl:value-of
select="format-number($numberVal,$ltCurFormat)"/>
                  </xsl:when>
                  <!--<xsl:when test="$numberVal =
'-1' or $numberVal = '0'">
                    <xsl:text
xml:space="preserve">-</xsl:text>
                  </xsl:when>-->
                  <xsl:when test="$numberVal = '-1' or
$numberVal = '0'">
                    <xsl:text
xml:space="preserve">-</xsl:text>
                  </xsl:when>
                  <xsl:when test="$style='percent'">
                    <xsl:value-of
select="format-number($numberVal * 100,'0.00')"/>
                    <xsl:text>%</xsl:text>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:value-of
select="format-number($numberVal,$ltNumFormat)"/>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:if>
              <xsl:if test="$numberVal = ''">
                <xsl:text
xml:space="preserve">-</xsl:text>
              </xsl:if>
            </div>
          </nobr>
        </td>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="Cell[not(*)]">
    <xsl:for-each select="$msrs">
      <td align="right" style="overflow:none">
        <nobr>
          <div style="width:80px">

          </div>
        </nobr>
      </td>
      <!--<td>&#xa0;</td>-->
    </xsl:for-each>
  </xsl:template>


  <!--== ROW DATA   ==-->


  <!--== Columns  ==-->
  <xsl:template match="Columns" mode="Column">
    <xsl:apply-templates select="ColGrp[1]"
mode="Header">
      <!-- 0 for top level heading, 1 to cut it out
-->
      <xsl:with-param name="depth">
        <xsl:choose>
          <xsl:when test="$axisHeads='true'">
            <xsl:value-of select="0"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="ColGrp" mode="Header">
    <xsl:param name="depth" />
    <tr>
      <!-- the very first row needs a padding cell -->
      <!--<xsl:for-each
select="//ColGrp[count(ancestor::ColGrp)=$depth]">-->
      <xsl:for-each select="key('c', $depth)">
        <!--<td colspan="{count(.//Col)*$msrs}"
align="center" style="overflow:none">-->
        <td colspan="{count(.//Col)*$nrmr}"
align="center" style="overflow:none">
          <nobr>
            <div>
              <xsl:value-of select="@heading"/>
            </div>
          </nobr>
        </td>
      </xsl:for-each>
    </tr>
    <xsl:choose>
      <xsl:when test="ColGrp">
        <xsl:apply-templates select="ColGrp[1]"
mode="Header">
          <xsl:with-param name="depth"
select="$depth+1" />
        </xsl:apply-templates>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="Col[1]"
mode="colHead" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="Col" mode="colHead">
    <tr>
      <xsl:for-each select="$colSet">
        <td colspan="{$nrmr}" valign="top"
align="center" style="overflow:none">
          <nobr>
            <div>
              <xsl:value-of select="@heading"/>
            </div>
          </nobr>
        </td>
      </xsl:for-each>
    </tr>
    <tr valign="bottom">
      <xsl:for-each select="$colSet">
        <xsl:apply-templates select="/Report/Measures"
mode ="ColumnMeasure">
          <xsl:with-param name="pos"
select="position()" />
        </xsl:apply-templates>
      </xsl:for-each>
    </tr>
  </xsl:template>

  <xsl:template match="Measures" mode="ColumnMeasure">
    <xsl:param name="pos" />
    <xsl:for-each select="$msrs">
      <xsl:variable name="mPos">
        <xsl:value-of select="position()" />
      </xsl:variable>
      <td align="center">
        <nobr>
          <div class="action" style="width:90px;
overflow:none"  onclick="sortFullGrid({$mPos}, {$pos},
'{@class}')">
            <xsl:value-of select="@heading"/>
          </div>
        </nobr>
      </td>
    </xsl:for-each>
  </xsl:template>

  <!--== Columns  ==-->


  <!--== ROW MODE HEADING  ==-->

  <xsl:template match="Row" mode="heading">
    <tr>
      <xsl:if test="preceding-sibling::Row=false() or
number($sortCol) != 0">
        <xsl:apply-templates select="parent::RowGrp"
mode="heading" />
      </xsl:if>
      <xsl:variable name="theHeading">
        <xsl:call-template name="js-escapeAmp">
          <xsl:with-param name="string">
            <xsl:call-template name="js-escapeApos">
              <xsl:with-param name="string"
select="@heading" />
            </xsl:call-template>
          </xsl:with-param>
        </xsl:call-template>
      </xsl:variable>    
      <td title="{$theHeading}" style="overflow:none">
        <nobr>
          <div>           
            <xsl:value-of select="$theHeading"/>
          </div>
        </nobr>
      </td>
    </tr>
  </xsl:template>
  
  
  <xsl:template match="RowGrp" mode="heading">
    <xsl:if test="preceding-sibling::RowGrp=false() or
number($sortCol) != 0">
      <xsl:apply-templates select="parent::RowGrp"
mode="heading" />
    </xsl:if>
    <xsl:if test="parent::Rows=false() or
$axisHeads='true'">
      <td style="overflow:none; vertical-align:top"
title="{@heading}" valign="top">
        <xsl:if test="number($sortCol) = 0">
          <xsl:attribute name="rowspan">
            <xsl:value-of select="count(.//Row)"/>
          </xsl:attribute>
        </xsl:if>
        <nobr>
          <div>
            <xsl:value-of select="@heading"/>
          </div>
        </nobr>
      </td>
    </xsl:if>
  </xsl:template>



  <xsl:template name="js-escapeApos">
    <xsl:param name="string" />
    <xsl:call-template name="substitute">
      <xsl:with-param name="string"
select="normalize-space($string)" />
      <xsl:with-param name="find"
select='"&amp;apos;"' />
      <xsl:with-param name="replace" select='"&apos;"'
/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="js-escapeAmp">
    <xsl:param name="string" />
    <xsl:call-template name="substitute">
      <xsl:with-param name="string"
select="normalize-space($string)" />
      <xsl:with-param name="find" select='"&amp;amp;"'
/>
      <xsl:with-param name="replace" select='"&amp;"'
/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="substitute">
    <xsl:param name="string" />
    <xsl:param name="find" />
    <xsl:param name="replace" />
    <xsl:choose>
      <xsl:when test="$find and $string and
contains($string, $find)">
        <xsl:value-of
select="substring-before($string, $find)" />
        <xsl:value-of select="$replace" />
        <xsl:call-template name="substitute">
          <xsl:with-param name="string"
select="substring-after($string,$find)" />
          <xsl:with-param name="find" select="$find"
/>
          <xsl:with-param name="replace"
select="$replace" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$string" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>


</xsl:stylesheet>





--- Manfred Staudinger <manfred.staudinger@xxxxxxxxx>
wrote:

> On 14/03/2008, Michael Kay <mike@xxxxxxxxxxxx>
> wrote:
> > > A lot has already been said and many suggestions
> have already
> >  > been made in this thread to speed up the
> processing of your
> >  > stylesheet, but since your stylesheet is rather
> >  > straightforward I doubt if any of the
> suggestions really



		
___________________________________________________________ 
Win a BlackBerry device from O2 with Yahoo!. Enter now. http://www.yahoo.co.uk/blackberry

Current Thread