0

I have a large XML file of this format:

<data jsxid="jsxroot" caseNumber="59878">
<record jsxid="1" poNumber="13-192208" manu="Biotronik" catNumber="101"  total="0" />
<record jsxid="2" poNumber="13-192208" manu="Biotronik" catNumber="102"  total="0" />               
<record jsxid="3" poNumber="13-192208" manu="Biotronik Total"  catNumber=""  total="1" />
<record jsxid="4" poNumber="13-192211" manu="Biotronik"  catNumber="103" total="0" />
<record jsxid="5" poNumber="13-192211" manu="Biotronik Total"  catNumber="" total="1"/>

I want to paginate it into groups of 25 or less, and each page must end on a total line (@total="1").

I got as far as inserting a static page number using the following XSL, but that sometimes cuts off in the middle of a poNumber group so the total is on the next page:

<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:param name="pagesize" select="25" />

<xsl:template match="data">
    <data>
        <xsl:for-each select="@*">                                    
            <xsl:copy-of select="." />                      
        </xsl:for-each>

        <xsl:apply-templates mode="page" select="record[position() mod $pagesize = 1]" />
    </data>
</xsl:template>

<xsl:template match="record" mode="page">

    <record jsxid="{position()}" >
        <xsl:apply-templates select=". | following-sibling::record[position() &lt; $pagesize]" />
    </record>       

</xsl:template>

<xsl:template match="record">
     <xsl:copy-of select="." />  
</xsl:template>

Any ideas on how to make sure the last record on each page is a total?

EDIT: I just wanted to clarify what I was doing with the stylesheet I posted. It inserts a placeholder for a page (jsxid = n) every ($pagesize) records. So if pagesize = 5 for a dataset similar to the one I posted, the output is:

<data jsxid="jsxroot" caseNumber="59878">
<record jsxid="1">
<record jsxid="1" poNumber="13-192208" manufacturer="Biotronik" catalogNumber="101"      total="0" />
<record jsxid="2" poNumber="13-192208" manufacturer="Biotronik" catalogNumber="102"  total="0" />
<record jsxid="3" poNumber="13-192208" manufacturer="Biotronik Total"  catalogNumber=""  total="1" />
<record jsxid="4" poNumber="13-192211" manufacturer="Biotronik"  catalogNumber="103" total="0" />
<record jsxid="5" poNumber="13-192211" manufacturer="Biotronik Total"  catalogNumber="" total="1"/>
<record jsxid="2">  
<record jsxid="6" poNumber="13-192208" manufacturer="Biotronik" catalogNumber="101"  total="0" />
<record jsxid="7" poNumber="13-192208" manufacturer="Biotronik" catalogNumber="102"  total="0" />


I'm displaying this data in a matrix in General Interface and using the jsxid to iterate between "pages."

Thanks.

1 Answers1

0

I got as far as inserting a static page number using the following XSL

I don't see that anywhere in the stylesheet you posted. Assuming by pagination you mean assigning a page number to each record, try:

XSLT 1.0

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:param name="pagesize" select="25" />

<xsl:key name="record" match="record" use="@poNumber" />

<xsl:template match="/data">
    <xsl:copy>
        <xsl:copy-of select="@*" />  
            <xsl:call-template name="paginate">
                <xsl:with-param name="orders" select="record[@total=1]"/>
                <xsl:with-param name="page" select="1"/>
                <xsl:with-param name="existing-lines" select="0"/>
        </xsl:call-template>    
    </xsl:copy>
</xsl:template>

<xsl:template name="paginate">
    <xsl:param name="orders" />
    <xsl:param name="page"/>
    <xsl:param name="existing-lines"/>
    <xsl:param name="current-order-records" select="key('record', $orders[1]/@poNumber)"/>
    <xsl:param name="added-lines" select="count($current-order-records)"/>
    <xsl:choose>
        <xsl:when test="$existing-lines + $added-lines > $pagesize">
            <xsl:call-template name="paginate">
                <xsl:with-param name="orders" select="$orders"/>
                <xsl:with-param name="page" select="$page + 1"/>
                <xsl:with-param name="existing-lines" select="0"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:for-each select="$current-order-records">
                <record page="{$page}">
                    <xsl:copy-of select="@*"/>  
                </record>
            </xsl:for-each>
            <xsl:if test="count($orders) > 1">
                <xsl:call-template name="paginate">
                    <xsl:with-param name="orders" select="$orders[position() > 1]"/>
                    <xsl:with-param name="page" select="$page"/>
                    <xsl:with-param name="existing-lines" select="$existing-lines + $added-lines "/>
                </xsl:call-template>
            </xsl:if>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Your input is too small to test this, so ... Note that this will crash if any poNumber has more than 25 records.


Edit:

Creating a container element for each page is considerably more difficult: a pre-processing pass will be required. Try:

<?xml version="1.0" encoding="utf-8"?>
<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:param name="pagesize" select="25" />

<xsl:key name="record" match="record" use="@poNumber" />
<xsl:variable name="root" select="/" />

<xsl:template match="/data">
    <!-- first-pass -->
    <xsl:variable name="index">
        <xsl:call-template name="paginate">
            <xsl:with-param name="orders" select="record[@total=1]"/>
            <xsl:with-param name="page" select="1"/>
            <xsl:with-param name="existing-lines" select="0"/>
        </xsl:call-template>    
    </xsl:variable>
    <xsl:variable name="index-entry" select="exsl:node-set($index)/entry" />
    <!-- output -->
    <xsl:copy>
        <xsl:copy-of select="@*" />  
        <xsl:for-each select="$index-entry[@page-start='true']">
            <record jsxid="{@page}">
                <xsl:variable name="poNumbers" select="$index-entry[@page=current()/@page]/@poNumber" />
                <xsl:for-each select="$root">
                    <xsl:copy-of select="key('record', $poNumbers)"/>
                </xsl:for-each>
            </record>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

<xsl:template name="paginate">
    <xsl:param name="orders" />
    <xsl:param name="page"/>
    <xsl:param name="existing-lines"/>
    <xsl:param name="current-order-records" select="key('record', $orders[1]/@poNumber)"/>
    <xsl:param name="added-lines" select="count($current-order-records)"/>
    <xsl:choose>
        <xsl:when test="$existing-lines + $added-lines > $pagesize">
            <xsl:call-template name="paginate">
                <xsl:with-param name="orders" select="$orders"/>
                <xsl:with-param name="page" select="$page + 1"/>
                <xsl:with-param name="existing-lines" select="0"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <entry page="{$page}" page-start="{$existing-lines = 0}">
                <xsl:copy-of select="$orders[1]/@*"/> 
            </entry>
            <xsl:if test="count($orders) > 1">
                <xsl:call-template name="paginate">
                    <xsl:with-param name="orders" select="$orders[position() > 1]"/>
                    <xsl:with-param name="page" select="$page"/>
                    <xsl:with-param name="existing-lines" select="$existing-lines + $added-lines "/>
                </xsl:call-template>
            </xsl:if>
        </xsl:otherwise>
     </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Edit 2:

To calculate the minimum page size as required by the largest PO, add this at the top level of your stylesheet (outside of any template):

<xsl:variable name="largestPO">
    <xsl:for-each select="/data/record[@total = 1]">
        <xsl:sort select="count(key('record', @poNumber))" data-type="number" order="descending"/>
        <xsl:if test="position()=1">
            <xsl:value-of select="count(key('record', @poNumber))" />  
        </xsl:if>
    </xsl:for-each>
</xsl:variable>

Now you just need to compare this to your default page size and pick the larger of the two.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Thanks, your help is greatly appreciated and I'll be able to work with this. I'm new here, so I don't know how to give you "credit" for the answer since I don't have enough reputation to upvote your answer. – Gabe Altenhofen Aug 19 '14 at 15:33
  • For completeness, I edited the initial post to describe what I was doing with the "static pagination" XSL that I posted since it wasn't very clear. – Gabe Altenhofen Aug 19 '14 at 15:46
  • @GabeAltenhofen "*I'm new here, so I don't know how to give you "credit" for the answer since I don't have enough reputation to upvote your answer.*" Just close the question by ticking the 'accept' box next to the answer. – michael.hor257k Aug 19 '14 at 16:09
  • I didn't specify this in the original question, but I'm wondering if you could help me get the data into the format that I edited at the bottom on the post. So instead of a page attribute on each record, a record hierarchy where and all records below it belong on that "page" until the next record where . All of the subsequent functions we use to display the data use this format and I thought that I could make your solution work, but I underestimated the amount of procedures we have. – Gabe Altenhofen Aug 20 '14 at 14:44
  • @GabeAltenhofen Yes, I am afraid this requirement raises the level of complexity significantly. See the edit to my post. – michael.hor257k Aug 20 '14 at 19:30
  • Apologies, I deleted my previous comment as the issue was (obviously) on my side. This works perfectly and is truly appreciated. You're a legend. – Gabe Altenhofen Aug 20 '14 at 22:19
  • Initially you asked if we can assume that no poNumber has more than 25 records and I responded with "yes." Of course, several weeks later I came upon a data set that exceeds these constraints. Would you be able to edit the solution so that IF the largest count of poNumbers is greater than 25, then set the page number to that count? I imagine that this, once again, raises the level of complexity... – Gabe Altenhofen Sep 12 '14 at 18:27