The answers to this question show possible ways to approach the problem:
xslt: How could I use xslt to create a table with multiple columns and rows?
EDIT: A solution that incorporates the techniques seen in the linked question follows.
I am assuming:
- your
@row and @col attributes are incrementing numbers that define the position of the record in the table, and they cannot really contain the string "n". As such they are not unique throughout the document, which makes them unsuitable as HTML @id attributes. I substituted them by @title attributes in my output.
- there are no implicit empty rows (gaps in
@row continuity will not produce empty rows), only implicit empty cells.
- every
@row and @col combination is unique.
This XSLT 1.0 transformation:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- prepare some keys for later use -->
<xsl:key name="kRecordsByRow" match="record" use="@row" />
<xsl:key name="kRecordsByPos" match="record" use="concat(@row, ',', @col)" />
<!-- find out the highest @col number -->
<xsl:variable name="vMaxCol">
<xsl:for-each select="/root/record">
<xsl:sort select="@col" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="@col" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- select the <record>s that are the first in their rows -->
<xsl:variable name="vRows" select="
/root/record[
generate-id()
=
generate-id(key('kRecordsByRow', @row)[1])
]
" />
<!-- output basic table structure -->
<xsl:template match="/root">
<table>
<xsl:for-each select="$vRows">
<xsl:sort select="@row" data-type="number" />
<tr title="{@row}">
<xsl:call-template name="td" />
</tr>
</xsl:for-each>
</table>
</xsl:template>
<!-- output the right number of <td>s in each row, empty or not -->
<xsl:template name="td">
<xsl:param name="col" select="1" />
<td title="{$col}">
<xsl:value-of select="key('kRecordsByPos', concat(@row, ',', $col))/@val" />
</td>
<xsl:if test="$col < $vMaxCol">
<xsl:call-template name="td">
<xsl:with-param name="col" select="$col + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
…when applied to this (slightly modified) input:
<root>
<record row="1" col="1" val="1" />
<record row="1" col="2" val="2" />
<record row="1" col="3" val="3" />
<record row="1" col="4" val="4" />
<record row="2" col="1" val="5" />
<record row="2" col="3" val="6" />
<record row="2" col="4" val="7" />
<record row="3" col="2" val="8" />
<record row="3" col="3" val="9" />
<record row="3" col="4" val="10" />
</root>
…produces:
<table>
<tr title="1">
<td title="1">1</td>
<td title="2">2</td>
<td title="3">3</td>
<td title="4">4</td>
</tr>
<tr title="2">
<td title="1">5</td>
<td title="2"></td>
<td title="3">6</td>
<td title="4">7</td>
</tr>
<tr title="3">
<td title="1"></td>
<td title="2">8</td>
<td title="3">9</td>
<td title="4">10</td>
</tr>
</table>
- Muenchian grouping is used to select the first
<record>s of each @row group
- an
<xsl:key> is used to pinpoint a record by it's position
- recursion is used to produce a consistent set of
<td>s, independent of the actual existence of a <record> at the named position