Subject: Re: [xsl] Table formatting challenge From: Oliver Becker <obecker@xxxxxxxxxxxxxxxxxxxxxxx> Date: Thu, 12 Jul 2001 17:39:12 +0200 (MET DST) |
> 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 <= ../../@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 |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] Table formatting challeng, David Carlisle | Thread | Re: [xsl] Table formatting challeng, David Carlisle |
[xsl] XML to HTML, Kevin_Gutch | Date | Re: [xsl] finding a bracket: "{", Johannes Döbler |
Month |