开发者

Re-grouping nodes in variable containing sequence of nodes

I am trying to create a two-column print-able HTML phone list from an XML file of organizational groups containing associated personnel

 <group print_order=1>
 <group_name><![CDATA[Human Resources]]></group_name>
    <person>...(name, address, phone data)...</person>
    <person> ... </person>
    <person> ... </person>
 </group>
 <group print_order=2>
 <group_name><![CDATA[Operations]]></group_name>
    <person> ... </person>
    <person> ... </person>
    <person> ... </person>
    <person> ... </person>
 </group>
 <group print_order=3>
 <group_name><![CDATA[IT Services]]></group_name>

etc.

I'm trying to get to the following html output

 <table>
  <tr>
   <td>
   <div class=group>Human Resources
    <person>
     <span class=name> lastname, firstname </span>
     <span class=addr> room# building </span>
     <span class=phone> 555-5555 </span>
    </person>
        (repeated for each person in group)
   </div>
   <div class=group>Operations
    <开发者_StackOverflow社区;person>
     <span class=name> lastname, firstname </span>
     <span class=addr> room# building </span>
     <span class=phone> 555-5555 </span>
    </person>
   </div>
  </td>
  <td>
   <div class=group>IT Services
    <person>
     <span class=name> lastname, firstname </span>
     <span class=addr> room# building </span>
     <span class=phone> 555-5555 </span>
    </person>
        (repeated for each person in group)
   </div>
     etc. 
  </td>
 </tr>
</table>
<p class=page_break>
<table>  (next table on next page)
 <tr>
  <td>  (next set of groups with people)
  </td>
  <td> ( next set of groups with people )
  </td>
  </tr>
</table>
(Repeat tables as needed until all groups output) 

The problem is that I want the number of lines in a table cell to be less than or equal to the number lines on a printed page, and I don't want groups split across table elements. I've tried the xsl:for-each-group instruction, but I can't figure out what the group-by or group-adjacent code should look like. I've also tried the following horrendous test:

<xsl:if test="sum($nodes/group[following-sibling::group[preceding-sibling::group[1] is current()]]/@line_count) mod 40 &gt; sum($nodes/group[following-sibling::group[preceding-sibling::group[2] is current()]]/@line_count) mod 40 ">

in a for-each loop, where nodes is a variable containing the sequence of groups, line_count is the number lines per group, and 40 is the # of lines per page. I'm using HTML tables to maximize browser support.

Is there a better way to do this? Using CSS maybe? Larry Wall, the author of the Perl language, said that in a programming language, easy things should be easy, and hard things should be possible. Is what I'm after possible in XSLT/XPath? Thanks to anyone willing to wade through all this.


I assume that each person takes a single line, and that no single group is too large to fit on one page. The following stylesheet paginates by recursively collecting groups into a variable ($currentColumn) as long as the total number of persons is lower than the desired line count. Columns are collected into another variable ($columns) as long as the total number of columns is lower than the desired column count. Once the column limit is reached, a page is output, and then the recursion continues with the next page.

<xsl:stylesheet version="2.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:param name="lineCount" select="25"/>
    <xsl:param name="columnCount" select="2"/>

    <xsl:template match="/">
        <html>
            <head>
                <title></title>
            </head>
            <body>
                <xsl:call-template name="paginate">
                    <xsl:with-param name="groups" select="/groups/group"/>
                </xsl:call-template>
            </body>
        </html>
    </xsl:template>

    <xsl:template name="paginate">
        <xsl:param name="groups"/>
        <xsl:param name="currentColumn" select="/.."/>
        <xsl:param name="columns" select="/.."/>
        <xsl:choose>
            <xsl:when test="not($groups)">
                <!-- End of input. Print current page and stop recursion. -->
                <xsl:variable name="column">
                    <column><xsl:sequence select="$currentColumn"/></column>
                </xsl:variable>
                <xsl:call-template name="print-page">
                    <xsl:with-param name="columns" select="$columns,$column"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="count($currentColumn//person) + count($groups[1]/person) le $lineCount">
                <xsl:call-template name="paginate">
                    <xsl:with-param name="currentColumn" select="$currentColumn,$groups[1]"/>
                    <xsl:with-param name="columns" select="$columns"/>
                    <xsl:with-param name="groups" select="$groups[position() gt 1]"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="count($groups[1]/person) gt $lineCount">
                <xsl:message terminate="yes">Too many persons in a group!</xsl:message>
            </xsl:when>
            <xsl:when test="count($columns) + 1 lt $columnCount">
                <!-- Start a new column -->
                <xsl:variable name="column">
                    <column><xsl:sequence select="$currentColumn"/></column>
                </xsl:variable>
                <xsl:call-template name="paginate">
                    <xsl:with-param name="columns" select="$columns,$column"/>
                    <xsl:with-param name="groups" select="$groups"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <!-- All columns full. Print current page and page break, then advance to next page. -->
                <xsl:variable name="column">
                    <column><xsl:sequence select="$currentColumn"/></column>
                </xsl:variable>
                <xsl:call-template name="print-page">
                    <xsl:with-param name="columns" select="$columns,$column"/>
                </xsl:call-template>
                <p class="page_break"/>
                <xsl:call-template name="paginate">
                    <xsl:with-param name="groups" select="$groups"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="print-page">
        <xsl:param name="columns"/>
        <table>
            <tr>
                <xsl:for-each select="$columns">
                    <td>
                        <xsl:apply-templates select="column/group"/>
                    </td>
                </xsl:for-each>
            </tr>
        </table>
    </xsl:template>

    <xsl:template match="group">
        <div class="group">
            <xsl:value-of select="group_name"/>
            <xsl:for-each select="person">
                <person>
                    <span class="name"><xsl:value-of select="name"/></span>
                    <span class="addr"><xsl:value-of select="addr"/></span>
                    <span class="phone"><xsl:value-of select="phone"/></span>
                </person>
            </xsl:for-each>
        </div>
    </xsl:template>

</xsl:stylesheet>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜