Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
280 views
in Technique[技术] by (71.8m points)

Please suggest for XSLT code for Table rowspan and colspan issues

<article>

<table id="tbl1">
<caption><p>Table 1. Sample Table</p></caption>

<thead>
<tr>
    <td colspan="3">I</td>
    <td colspan="4">II</td>
</tr>

<tr>
    <td>Sl. No.</td>
    <td>Name</td>
    <td>Place</td>
    <td>Subject 1</td>
    <td>Subject 2</td>
    <td>Subject 3</td>
    <td>Grade</td>
</tr>
</thead>

<tbody>
<tr>
    <td rowspan="3">1</td>
    <td colspan="2">Kishan</td>
    <td>95</td>
    <td>96</td>
    <td rowspan="2">97</td>
    <td>A</td>
</tr>

<tr>

    <td>Kishan</td>
    <td>Bangalore</td>
    <td>94</td>
    <td>96</td>

    <td>A</td>
</tr>

<tr>

    <td>Likhith</td>
    <td>Bhadravathi</td>
    <td>94</td>
    <td>94</td>
    <td>99</td>
    <td>A</td>
</tr>
</tbody>
</table>

</article>

Required OutPut: If colspan is 2 is coded in second cell, then third should not be there, next cell name should be "colname="3" (start Index is 0). Same for rowspan, if present row's first cell having rowspan="3", then next two rows should not have colname="0", those next two rows start cells will have the name="1" (start index is 0, thats why 1 means second cell). Please suggest for the XSLT coding two address both rowspan and colspan present in same table.

    <article>

<table id="tbl1">
<caption><p>Table 1. Sample Table</p></caption>

<thead>
<tr>
    <td colname="0:0">I</td>
    <td colname="0:3">II</td>
</tr>

<tr>
    <td colname="1:0">Sl. No.</td>
    <td colname="1:1">Name</td>
    <td colname="1:2">Place</td>
    <td colname="1:3">Subject 1</td>
    <td colname="1:4">Subject 2</td>
    <td colname="1:5">Subject 3</td>
    <td colname="1:6">Grade</td>
</tr>
</thead>


<tbody>
<tr>
    <td colname="2:0">1</td>
    <td colname="2:1">Kishan</td>
    <td colname="2:3">95</td>
    <td colname="2:4">96</td>
    <td colname="2:5">97</td>
    <td colname="2:6">A</td>
</tr>

<tr>

    <td colname="3:1">Kishan</td>
    <td colname="3:2">Bangalore</td>
    <td colname="3:3">94</td>
    <td colname="3:4">96</td>

    <td colname="3:6">A</td>
</tr>

<tr>

    <td colname="4:1">Likhith</td>
    <td colname="4:2">Bhadravathi</td>
    <td colname="4:3">94</td>
    <td colname="4:4">94</td>
    <td colname="4:5">99</td>
    <td colname="4:6">A</td>
</tr>
</tbody>
</table>

</article>

XSLT code from StackOverFlow site:

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

<!--rowspan in table with xslt-->
<xsl:template match="TABLE2">
  <tbody>
    <xsl:call-template name="processRows">
      <xsl:with-param name="rows" select="ROW"/>
    </xsl:call-template>
  </tbody>
</xsl:template>

<xsl:template name="processRows">
  <xsl:param name="rows"/>
  <xsl:param name="index" select="1"/>
  <!-- Bit vector for the columns -->
  <xsl:param name="col1" select="0"/>
  <xsl:param name="col2" select="0"/>
  <xsl:param name="col3" select="0"/>
  <xsl:param name="col4" select="0"/>
  <xsl:param name="col5" select="0"/>
  <xsl:param name="col6" select="0"/>

  <xsl:variable name="cellsBefore2">
    <xsl:choose>
      <xsl:when test="$col1 > 0">0</xsl:when>
      <xsl:otherwise>1</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="cellsBefore3">
    <xsl:choose>
      <xsl:when test="$col2 > 0">
        <xsl:value-of select="$cellsBefore2"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore2 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

   <xsl:variable name="cellsBefore4">
    <xsl:choose>
       <xsl:when test="$col3 > 0">
        <xsl:value-of select="$cellsBefore3"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore3 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

   <xsl:variable name="cellsBefore5">
    <xsl:choose>
       <xsl:when test="$col4 > 0">
        <xsl:value-of select="$cellsBefore4"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore4 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="cellsBefore6">
    <xsl:choose>
       <xsl:when test="$col5 > 0">
        <xsl:value-of select="$cellsBefore5"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore5 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>



  <row>
    <xsl:if test="$col1 = 0">
      <entry colname="1">
        <xsl:value-of select="$rows[$index]/CELL[1]/text()"/>
      </entry>
    </xsl:if>
    <xsl:if test="$col2 = 0">
      <entry colname="2">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore2 + 1]/text()"/>
      </entry>
    </xsl:if>
    <xsl:if test="$col3 = 0">
      <entry colname="3">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore3 + 1]/text()"/>
      </entry>
    </xsl:if>
    <xsl:if test="$col4 = 0">
      <entry colname="4">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore4 + 1]/text()"/>
      </entry>
    </xsl:if>
     <xsl:if test="$col5 = 0">
      <entry colname="5">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore5 + 1]/text()"/>
      </entry>
    </xsl:if>
     <xsl:if test="$col6 = 0">
      <entry colname="6">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore6 + 1]/text()"/>
      </entry>
    </xsl:if>


  </row>
  <xsl:if test="$index &lt; count($rows)">
    <xsl:call-template name="processRows">
      <xsl:with-param name="rows" select="$rows"/>
      <xsl:with-param name="index" select="$index + 1"/>
      <xsl:with-param name="col1">
        <xsl:choose>
          <xsl:when test="$col1 > 0">
            <xsl:value-of select="$col1 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col2">
        <xsl:choose>
          <xsl:when test="$col2 > 0">
            <xsl:value-of select="$col2 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore2 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col3">
        <xsl:choose>
          <xsl:when test="$col3 > 0">
            <xsl:value-of select="$col3 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore3 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col4">
        <xsl:choose>
          <xsl:when test="$col4 > 0">
            <xsl:value-of select="$col4 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore4 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col5">
        <xsl:choose>
          <xsl:when test="$col5 > 0">
            <xsl:value-of select="$col5 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore5 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
       <xsl:with-param name="col6">
        <xsl:choose>
          <xsl:when test="$col6 > 0">
            <xsl:value-of select="$col6 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore6 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>


    </xsl:call-template>
  </xsl:if>
</xsl:template>
</xsl:stylesheet> 
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is not at all simple. Basically, you are asking how to render an HTML table visually, by positioning each cell on a (equi-spaced) x-y grid of rows and columns.

This is complex, because the position of each cell depends not only on the width (colspan) of the preceding cells in the same row, but also on the position of cells in preceding rows that span more than one row. This position is not known before the preceding cells themselves have been processed - so this is a giant render-as-you-go cascading operation.

Due to this complexity, I suggest solving the basic problem in isolation first, before introducing additional constraints (e.g. separate header rows or numbers starting from 0).

For testing, I have used the following table1 as the input:

<table border="1">
    <tr>
        <td>Column 1</td>
        <td>Column 2</td>
        <td>Column 3</td>
    </tr>
    <tr>
        <td rowspan="2">A</td>
        <td colspan="2">B</td>
    </tr>
    <tr>
        <td>C</td>
        <td>D</td>
    </tr>
    <tr>
        <td>E</td>
        <td rowspan="2" colspan="2">F</td>
    </tr>
    <tr>
        <td>G</td>
    </tr>
    <tr>
        <td colspan="3">H</td>
    </tr>
</table>

Applying the folowing stylesheet:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/table">
    <table>
        <xsl:text>&#10;</xsl:text>
        <xsl:call-template name="generate-rows">
            <xsl:with-param name="current-row" select="1"/>
            <xsl:with-param name="last-row" select="count(tr)"/>
        </xsl:call-template>
    </table>
</xsl:template>


<xsl:template name="generate-rows">
    <xsl:param name="current-row"/>
    <xsl:param name="last-row"/>
    <xsl:param name="result" select="some-dummy-node-to-make-this-a-node"/>

    <!-- append current-row to previous result -->
    <xsl:variable name="new-result">
        <xsl:copy-of select="$result"/>
        <row num="{$current-row}">
            <xsl:text>&#10;</xsl:text>
            <!-- generate cells for current-row -->
            <xsl:call-template name="generate-cells">
                <xsl:with-param name="current-row" select="$current-row"/>
                <xsl:with-param name="current-cell" select="1"/>
                <xsl:with-param name="x" select="1"/>
                <xsl:with-param name="last-cell" select="count(tr[$current-row]/td)"/>
                <xsl:with-param name="previous-rows" select="$result"/>
            </xsl:call-template>
        </row>
        <xsl:text>&#10;</xsl:text>
    </xsl:variable>

    <xsl:choose>
        <xsl:when test="$current-row &lt; $last-row">
            <!-- recursive call -->
            <xsl:call-template name="generate-rows">
                <xsl:with-param name="current-row" select="$current-row + 1"/>
                <xsl:with-param name="last-row" select="$last-row"/>
                <xsl:with-param name="result" select="$new-result"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <!-- return result -->
            <xsl:copy-of select="$new-result"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>


<xsl:template name="generate-cells">
    <xsl:param name="current-row"/>
    <xsl:param name="current-cell"/>
    <xsl:param name="x"/>
    <xsl:param name="last-cell"/>
    <xsl:param name="previous-rows"/>

    <xsl:variable name="my-cell" select="tr[$current-row]/td[$current-cell]" />

    <xsl:choose>
        <!-- if there's a collision, move one place to the right -->
        <xsl:when test="exsl:node-set($previous-rows)/row/cell[@x &lt;= $x and @x + @width > $x and @y + @height > $current-row]">
            <xsl:call-template name="generate-cells">
                <xsl:with-param name="current-row" select="$current-row"/>
                <xsl:with-param name="current-cell" select="$current-cell"/>
                <xsl:with-param name="x" select="$x + 1"/>
                <xsl:with-param name="last-cell" select="$last-cell"/>
                <xsl:with-param name="previous-rows" select="$previous-rows"/>
            </xsl:call-template>
        </xsl:when>

        <xsl:otherwise>     
            <xsl:variable name="width">
                <xsl:choose>
                    <xsl:when test="$my-cell/@colspan">
                        <xsl:value-of select="$my-cell/@colspan"/>
                    </xsl:when>
                    <xsl:otherwise>1</xsl:otherwise>
                </xsl:choose>
            </xsl:variable>

             <xsl:variable name="height">
                <xsl:choose>
                    <xsl:when test="$my-cell/@rowspan">
                        <xsl:value-of select="$my-cell/@rowspan"/>
                    </xsl:when>
                    <xsl:otherwise>1</xsl:otherwise>
                </xsl:choose>
             </xsl:variable>

            <xsl:text>&#9;</xsl:text>
             <cell num="{$current-cell}" y="{$current-row}" x="{$x}" width="{$width}" height="{$height}">
                 <xsl:value-of select="$my-cell"/>
             </cell>
            <xsl:text>&#10;</xsl:text>

            <xsl:if test="$current-cell &lt; $last-cell">
                <xsl:call-template name="generate-cells">
                    <xsl:with-param name="current-row" select="$current-row"/>
                    <xsl:with-param name="current-cell" select="$current-cell + 1"/>
                    <xsl:with-param name="x" select="$x + $width"/>
                    <xsl:with-param name="last-cell" select="count(tr[$current-row]/td)"/>
                    <xsl:with-param name="previous-rows" select="$previous-rows"/>
                </xsl:call-template>
            </xsl:if>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

produces the following result:

<?xml version="1.0" encoding="UTF-8"?>
<table>
<row num="1">
    <cell num="1" y="1" x="1" width="1" height="1">Column 1</cell>
    <cell num="2" y="1" x="2" width="1" height="1">Column 2</cell>
    <cell num="3" y="1" x="3" width="1" height="1">Column 3</cell>
</row>
<row num="2">
    <cell num="1" y="2" x="1" width="1" height="2">A</cell>
    <cell num="2" y="2" x="2" width="2" height="1">B</cell>
</row>
<row num="3">
    <cell num="1" y="3" x="2" width="1" height="1">C</cell>
    <cell num="2" y="3" x="3" width="1" height="1">D</cell>
</row>
<row num="4">
    <cell num="1" y="4" x="1" width="1" height="1">E</cell>
    <cell num="2" y="4" x="2" width="2" height="2">F</cell>
</row>
<row num="5">
    <cell num="1" y="5" x="1" width="1" height="1">G</cell>
</row>
<row num="6">
    <cell num="1" y="6" x="1" width="3" height="1">H</cell>
</row>
</table>

As you can see, the x-y positioning of each cell corresponds to the visual rendering of the original table in a browser:

enter image description here

--
1. From http://en.wikipedia.org/wiki/Help:Table


For XSLT 2.0:

Change the stylesheet declaration to:

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

Change line #66 to:

<xsl:when test="$previous-rows/row/cell[@x &lt;= $x and @x + @width > $x and @y + @height > $current-row]">

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...