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

Re: Table formatting challenge

Subject: Re: Table formatting challenge
From: Oliver Becker <obecker@xxxxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 12 Jul 2001 17:39:12 +0200 (MET DST)
html table td sum cols
> Consider the following table:
> 
> +--------+--------+--------+--------+
> |        |   B    |        |   D    |
> +   A    +--------+   C    +--------+
> |        |        |        |   E    |
> +--------+--------+--------+--------+
> 
> Represented by this markup:
> 
> <informaltable>
> <tgroup cols="4">
> <colspec colname="c4" colnum="4"/>
> <tbody>
> <row>
>   <entry morerows="1">A</entry>
>   <entry>B</entry>
>   <entry morerows="1">C</entry>
>   <entry>D</entry>
> </row>
> <row>
>   <entry namest="c4">E</entry>
> </row>
> </tbody>
> </tgroup>
> </informaltable>
> 
> The correct HTML for this table is:
> 
> <table border="1">
> <tr>
>   <td rowspan="2">A</td>
>   <td>B</td>
>   <td rowspan="2">C</td>
>   <td>D</td>
> </tr>
> <tr>
>   <td class="auto-generated"></td>
>   <td>E</td>
> </tr>
> </table>
> 
> Can anyone see a practical way to achieve this result without
> resorting to extension functions? The tricky bit is not simply
> noticing that the introduction of namest may require some
> auto-generated cells to be inserted, but calculating correctly, in the
> presence of overhang (from an arbitrarily large number of preceeding
> rows), how many cells to insert.
> 
> I'm hoping I've overlooked something clever and elegant.

Evaluate it yourself ;-)
Tested with Saxon and Xalan, doesn't need extensions.
I hope my comments are reasonable useful.

Cheers,
Oliver



<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="html" />


<xsl:template match="informaltable">
   <table border="1">
      <xsl:apply-templates />
   </table>
</xsl:template>


<xsl:template match="row">
   <tr>
      <xsl:call-template name="process-row">
         <xsl:with-param name="col" select="1" />
      </xsl:call-template>
   </tr>
</xsl:template>


<!-- 
 | key for the colspec element;
 | not optimal, because colnames don't have to be globally unique, I assume
 +-->
<xsl:key name="colspec" match="colspec" use="@colname" />


<!--
 | Process each column of a row 
 +-->
<xsl:template name="process-row">
   <xsl:param name="col" />
   <!-- is the current column number <= total number of cols? -->
   <xsl:if test="$col &lt;= ../../@cols">
      <!-- compute the overhang for this column -->
      <xsl:variable name="overhang">
         <xsl:call-template name="compute-overhang">
            <xsl:with-param name="col" select="$col" />
            <xsl:with-param name="rows" select="preceding-sibling::row" />
         </xsl:call-template>
      </xsl:variable>
      <!-- only if $overhang is 0 we have to create a <td> -->
      <xsl:if test="$overhang = 0">
         <!-- find out, if there's an explicit entry in the input -->
         <!-- perhaps one with a namest attribute? -->
         <xsl:variable name="namest" 
              select="entry[key('colspec',@namest)/@colnum = $col]" />
         <xsl:choose>
            <xsl:when test="$namest">
               <!-- you may optimize this if you like and discard
                    rowspan="1" attributes ;-) -->
               <td rowspan="{sum($namest/@morerows)+1}">
                  <xsl:value-of select="$namest" />
               </td>
            </xsl:when>
            <xsl:otherwise>
               <!-- look for an entry at the requested position -->
               <xsl:variable name="entry-id">
                  <xsl:call-template name="find-entry">
                     <xsl:with-param name="col" select="$col" />
                     <xsl:with-param name="entries" select="entry" />
                  </xsl:call-template>
               </xsl:variable>
               <xsl:variable name="entry"
                             select="entry[generate-id()=$entry-id]" />
               <xsl:choose>
                  <xsl:when test="$entry">
                     <td rowspan="{sum($entry/@morerows)+1}">
                        <xsl:value-of select="$entry" />
                     </td>
                  </xsl:when>
                  <xsl:otherwise>
                     <td class="auto-generated" />
                  </xsl:otherwise>
               </xsl:choose>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:if>
      <!-- next column -->
      <xsl:call-template name="process-row">
         <xsl:with-param name="col" select="$col + 1" />
      </xsl:call-template>
   </xsl:if>
</xsl:template>


<!-- 
 | Compute the overhang for a given column 
 +-->
<xsl:template name="compute-overhang">
   <xsl:param name="col" />                 <!-- column number -->
   <xsl:param name="rows" />                <!-- preceding rows -->
   <xsl:param name="overhang" select="0" /> <!-- current overhang -->
   <xsl:choose>
      <xsl:when test="not($rows)">
         <!-- no rows left -->
         <xsl:value-of select="$overhang" />
      </xsl:when>
      <xsl:when test="$overhang > 0">
         <!-- we have already an overhang, go to the next row -->
         <xsl:call-template name="compute-overhang">
            <xsl:with-param name="col" select="$col" />
            <xsl:with-param name="rows" select="$rows[position() != 1]" />
            <xsl:with-param name="overhang" select="$overhang - 1" />
         </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
         <!-- ok, current overhang is 0, we have still rows;
              let's look for an entry in our column -->
         <xsl:variable name="namest" select=
            "$rows[1]/entry[key('colspec', @namest)[@colnum = $col]]" />
         <xsl:choose>
            <xsl:when test="$namest">
               <!-- there is an <entry namest=...> element -->
               <xsl:call-template name="compute-overhang">
                  <xsl:with-param name="col" select="$col" />
                  <xsl:with-param name="rows" select="$rows[position() != 1]" />
                  <xsl:with-param name="overhang" 
                                  select="sum($namest/@morerows)" />
               </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
               <xsl:variable name="entry-id">
                  <xsl:call-template name="find-entry">
                     <xsl:with-param name="col" select="$col" />
                     <xsl:with-param name="entries" select="$rows[1]/entry" />
                  </xsl:call-template>
               </xsl:variable>
               <xsl:variable name="entry"
                    select="$rows[1]/entry[generate-id()=$entry-id]" />
               <xsl:choose>
                  <xsl:when test="$entry">
                     <xsl:call-template name="compute-overhang">
                        <xsl:with-param name="col" select="$col" />
                        <xsl:with-param name="rows" 
                                        select="$rows[position() != 1]" />
                        <xsl:with-param name="overhang" 
                                        select="sum($entry/@morerows)" />
                     </xsl:call-template>
                  </xsl:when>
                  <xsl:otherwise>
                     <!-- an auto-generated cell must be created at the end;
                          it has no overhang -->
                     <xsl:call-template name="compute-overhang">
                        <xsl:with-param name="col" select="$col" />
                        <xsl:with-param name="rows" 
                                        select="$rows[position() != 1]" />
                     </xsl:call-template>
                  </xsl:otherwise>
               </xsl:choose>   
            </xsl:otherwise>
         </xsl:choose>   
      </xsl:otherwise>
   </xsl:choose>
</xsl:template>


<!-- 
 | Finds an entry for a requested column;
 | returns an id (generate-id) of this entry, since we cannot return a node
 +-->
<xsl:template name="find-entry">
   <xsl:param name="col" />     <!-- requested col, relative to $entries -->
   <xsl:param name="entries" /> <!-- left entry elements -->
   <xsl:choose>
      <!-- the first entry doesn't have a namest attribute, touche! -->
      <xsl:when test="$col=1 and $entries[1][not(@namest)]">
         <xsl:value-of select="generate-id($entries[1])" />
      </xsl:when>
      <!-- dito, except that we don't look for the first entry,
           process the rest recursively -->
      <xsl:when test="$col>1 and $entries[1][not(@namest)]">
         <xsl:call-template name="find-entry">
            <xsl:with-param name="col" select="$col - 1" />
            <xsl:with-param name="entries" select="$entries[position() != 1]" />
         </xsl:call-template>
      </xsl:when>
      <!-- the first entry has a namest attribute, `move' $col the
           appropriate number of steps and process the rest recursively -->
      <xsl:when test="$entries[1][@namest]">
         <xsl:call-template name="find-entry">
            <xsl:with-param name="col" 
                 select="$col - key('colspec', $entries[1]/@namest)/@colnum" />
            <xsl:with-param name="entries" select="$entries[position() != 1]" />
         </xsl:call-template>
      </xsl:when>
      <!-- otherwise, i.e. $col < 1 or $entries is empty, 
           return nothing -->
   </xsl:choose>
</xsl:template>


<xsl:template match="text()" />


</xsl:stylesheet>


/-------------------------------------------------------------------\
|  ob|do        Dipl.Inf. Oliver Becker                             |
|  --+--        E-Mail: obecker@xxxxxxxxxxxxxxxxxxxxxxx             |
|  op|qo        WWW:    http://www.informatik.hu-berlin.de/~obecker |
\-------------------------------------------------------------------/


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


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.