Summing based on matching values XSLT-1.0

83 views Asked by At

I'm currently trying to work with grouping in XSLT 1.0. I have XML similar to this:

<table>
    <row>
        <PRODUCER type="VARCHAR" value="PRODUCER 1"/>
        <PUBLICATION_CODE_-_NAME type="VARCHAR" value="PUBLICATION A"/>
        <DOMESTIC type="DECIMAL" value="20"/>
        <FOREIGN type="DECIMAL" value="4"/>
    </row>
    <row>
        <PRODUCER type="VARCHAR" value="PRODUCER 1"/>
        <PUBLICATION_CODE_-_NAME type="VARCHAR" value="PUBLICATION B"/>
        <DOMESTIC type="DECIMAL" value="57"/>
        <FOREIGN type="DECIMAL" value="10"/>
    </row>
    <row>
        <PRODUCER type="VARCHAR" value="PRODUCER 2"/>
        <PUBLICATION_CODE_-_NAME type="VARCHAR" value="PUBLICATION C"/>
        <DOMESTIC type="DECIMAL" value="35"/>
        <FOREIGN type="DECIMAL" value="20"/>
    </row>
    <row>
        <PRODUCER type="VARCHAR" value="PRODUCER 2"/>
        <PUBLICATION_CODE_-_NAME type="VARCHAR" value="PUBLICATION D"/>
        <DOMESTIC type="DECIMAL" value="23"/>
        <FOREIGN type="DECIMAL" value="18"/>
    </row>
</table>

So far I have been able to achieve everything I was hoping to except for get a sum of the DOMESTIC and FOREIGN based on the PRODUCER. I have read into Muenchian grouping and the likes but I am having trouble applying it to my XML. I believe that I have to create create a key such as the following based on PRODUCER.

<xsl:key name="producerkey" match="/table/row/" use="PRODUCER/@value"/>

It is at this point that I run into an issue. I believe that I have to generate ids and use those ids to group my values.

So far I am able to generate a PDF similar to this.

| PRODUCER || PUBLICATION ||DOMESTIC||FOREIGN|
------------------------------------------
|PRODUCER 1||PUBLICATION A||   20   ||   4   |
|          ||PUBLICATION B||   57   ||  10   | 
|          ||TOTALS       || DTOTAL || FTOTAL|
|PRODUCER 2||PUBLICATION C||   35   ||  20   |
|          ||PUBLICATION D||   23   ||  18   |
|          ||TOTALS       || DTOTAL || FTOTAL|

I am trying to replace the "DTOTAL" and "FTOTAL" by the sums of the DOMESTIC and FOREIGN columns with the appropriate PRODUCER grouping.

The following are what I thought we be the most relevant portions of my working XSLT that generates to layout similar to the table above.

<xsl:template match="row">
    <fo:table>
        <fo:table-body  font-size="10pt"
                        font-family="sans-serif"
                        line-height="10pt"
                        space-after.optimum="3pt">
            <xsl:for-each select="current()">
                <xsl:variable name="testnext" select="following-sibling::*[1]"/>
                <xsl:choose>
                    <xsl:when test="$testnext">
                        <xsl:choose>
                            <xsl:when test="$testnext/PRODUCER/@value = child::PRODUCER/@value">
                                <fo:table-row>
                                    <xsl:apply-templates select="PRODUCER"/>
                                    <xsl:apply-templates select="PUBLICATION_CODE_-_NAME"/>
                                    <xsl:apply-templates select="DOMESTIC"/>
                                    <xsl:apply-templates select="FOREIGN"/>
                                </fo:table-row>
                            </xsl:when>
                            <xsl:otherwise>
                                <fo:table-row>
                            <xsl:apply-templates select="PRODUCER"/>
                            <xsl:apply-templates select="PUBLICATION_CODE_-_NAME"/>
                            <xsl:apply-templates select="DOMESTIC"/>
                            <xsl:apply-templates select="FOREIGN"/>
                        </fo:table-row> 
                        <fo:table-row>
                            <fo:table-cell  width="2.125in"
                                             height="0.4in">
                                <fo:block>
                                    <fo:leader/>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell  width="3.25in"
                                            height="0.4in">
                                <fo:block>
                                    PRODUCER TOTAL
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell  width="0.95in"
                                            height="0.4in">
                                <fo:block>
                                    DTOTAL
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell  width="0.95in"
                                            height="0.4in">
                                <fo:block>
                                    FTOTAL
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:when>
                    <xsl:otherwise>
                        <fo:table-row>
                            <xsl:apply-templates select="PRODUCER"/>
                            <xsl:apply-templates select="PUBLICATION_CODE_-_NAME"/>
                            <xsl:apply-templates select="DOMESTIC"/>
                            <xsl:apply-templates select="FOREIGN"/>
                        </fo:table-row> 
                        <fo:table-row>
                            <fo:table-cell  width="2.125in"
                                             height="0.4in">
                                <fo:block>
                                    <fo:leader/>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell  width="3.25in"
                                            height="0.4in">
                                <fo:block>
                                    PRODUCER TOTAL
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell  width="0.95in"
                                            height="0.4in">
                                <fo:block>
                                    DTOTAL
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell  width="0.95in"
                                            height="0.4in">
                                <fo:block>
                                    FTOTAL
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </fo:table-body>
    </fo:table>
</xsl:template>

<xsl:template match="PRODUCER">
    <fo:table-cell  width="2.125in"
                    height="0.2in">
        <fo:block>
            <xsl:variable name="test" select="parent::row/preceding-sibling::row[1]"/>
            <xsl:choose>
                <xsl:when test="$test">
                    <xsl:choose>
                        <xsl:when test="$test/PRODUCER/@value = @value">
                            <fo:leader/>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="@value"/>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="@value"/>
                </xsl:otherwise>
            </xsl:choose>
        </fo:block>
    </fo:table-cell>
</xsl:template>

<xsl:template match="PUBLICATION_CODE_-_NAME">
    <fo:table-cell  width="3.25in"
                    height="0.2in">
        <fo:block>
            <xsl:value-of select="@value"/>
        </fo:block>
    </fo:table-cell>
</xsl:template>

<xsl:template match="DOMESTIC">
    <fo:table-cell  width="0.95in"
                    height="0.2in">
        <fo:block>
            <xsl:value-of select="@value"/>
        </fo:block>
    </fo:table-cell>
</xsl:template>

<xsl:template match="FOREIGN">
    <fo:table-cell  width="0.95in"
                    height="0.2in">
        <fo:block>
            <xsl:value-of select="@value"/>
        </fo:block>
    </fo:table-cell>
</xsl:template>

The basis of what I'm doing is to check each row for it's PRODUCER and use that comparison to determine when to include the PRODUCER value in the first column and when to create the row that includes the sums of the columns and the additional space. I'm sure my code is a bit messy but I've just began working with this technology and have found the learning curve to be a bit steep to self teach. Additionally, if it is the very first row then the PRODUCER is included, and if it is the last row then the totaling row will be included next to finish off the report. For what it is worth the producers are already grouped in the appropriate order.

Any and all help/advice/criticism would be very appreciated.

1

There are 1 answers

5
David Carlisle On BEST ANSWER

This is so much easier in XSLT 2 but for old time's sake:

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


 <xsl:key name="producerkey" match="row" use="PRODUCER/@value"/>

 <xsl:template match="table">
  <table>
   <thead>
    <tr>
     <th>Producer</th>
     <th>Publication</th>
     <th>Domestic</th>
     <th>Foreigh</th>
    </tr>
   </thead>
   <tbody>
    <xsl:for-each select="row[
              generate-id(.)
              =
              generate-id(key('producerkey',PRODUCER/@value))[1]
              ]">
     <xsl:for-each select="key('producerkey',PRODUCER/@value)">
      <tr>
       <td>-
       <xsl:if test="position()=1">
    <xsl:value-of select="PRODUCER/@value"/>
       </xsl:if>
       </td>
       <td><xsl:value-of select="PUBLICATION_CODE_-_NAME/@value"/></td>
       <td><xsl:value-of select="DOMESTIC/@value"/></td>
       <td><xsl:value-of select="FOREIGN/@value"/></td>
      </tr>
     </xsl:for-each>
     <tr>
      <td>-</td>
      <td>Totals</td>
      <td><xsl:value-of select="sum(key('producerkey',PRODUCER/@value)/DOMESTIC/@value)"/></td>
      <td><xsl:value-of select="sum(key('producerkey',PRODUCER/@value)/FOREIGN/@value)"/></td>
     </tr>
    </xsl:for-each>
   </tbody>
  </table>
 </xsl:template>

</xsl:stylesheet>

produces

<table><thead>
    <tr><th>Producer</th><th>Publication</th><th>Domestic</th><th>Foreigh</th></tr></thead><tbody>
    <tr>
      <td>-PRODUCER 1</td><td>PUBLICATION A</td><td>20</td><td>4</td></tr> 
<tr><td>-</td><td>PUBLICATION B</td><td>57</td><td>10</td></tr>
<tr><td>-</td><td>Totals</td><td>77</td><td>14</td></tr>
<tr><td>-
       PRODUCER 2</td><td>PUBLICATION C</td><td>35</td><td>20</td></tr>
<tr><td>-</td><td>PUBLICATION D</td><td>23</td><td>18</td></tr>
    <tr><td>-</td><td>Totals</td><td>58</td><td>38</td></tr>
  </tbody>
</table>