I would like to code an XSLT to convert an XML document to an XML-FO document, in order to generate a PDF.
This Image shows the current Output
So my Table has 3 columns. In the first 4 lines the second and third columns are merged.
My XML document looks like this:
<WS-Standards>
<tags>
<tag category = "parameters" > <!-- WS_Beer_Type -->
<tag_name>WS_Beer_TypeX</tag_name> <!-- browsename -->
<tag_number>30004</tag_number>
<datatype>Unsigned32</datatype>
<accessrights>RW</accessrights>
<names>
<name language="DE">Biersorte</name>
<name language="EN">Beer Type</name>
</names>
</tag>
</tags>
</WS-Standards>
My current XSLT document:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes"/>
<xsl:template match="WS-Standards">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master page-height="297mm" page-width="210mm"
margin="5mm 25mm 5mm 25mm" master-name="PageMaster">
<fo:region-body margin="20mm 0mm 20mm 0mm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="PageMaster">
<fo:flow flow-name="xsl-region-body">
<fo:block>
<xsl:apply-templates select="tags"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="tags">
<fo:block>
<xsl:apply-templates select="tag"/>
</fo:block>
</xsl:template>
<xsl:template match="tag">
<fo:block space-before="6pt" border-top="3pt solid green">
<fo:table>
<fo:table-column column-number="1" column-width="20%" border-style="solid"
border-width="1pt"/>
<fo:table-column column-number="2" column-width="10%" border-style="solid"
border-width="1pt"/>
<fo:table-column column-number="3" column-width="70%" border-style="solid"
border-width="1pt"/>
<fo:table-body>
<xsl:apply-templates/>
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
<xsl:template match="tag_name">
<fo:table-row>
<fo:table-cell column-number="1" border="1pt solid black">
<fo:block> Tag Name </fo:block>
</fo:table-cell>
<fo:table-cell column-number="2" number-columns-spanned="2" border="1pt solid black">
<fo:block>
<xsl:value-of select="."/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="tag_number">
<fo:table-row>
<fo:table-cell column-number="1" border="1pt solid black">
<fo:block> Tag-Nummer </fo:block>
</fo:table-cell>
<fo:table-cell column-number="2" number-columns-spanned="2" border="1pt solid black">
<fo:block>
<xsl:value-of select="."/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="datatype">
<fo:table-row>
<fo:table-cell column-number="1" border="1pt solid black">
<fo:block> Datentyp </fo:block>
</fo:table-cell>
<fo:table-cell column-number="2" number-columns-spanned="2" border="1pt solid black">
<fo:block>
<xsl:value-of select="."/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="accessrights">
<fo:table-row>
<fo:table-cell column-number="1" border="1pt solid black">
<fo:block> Zugriffsrechte </fo:block>
</fo:table-cell>
<fo:table-cell column-number="2" number-columns-spanned="2" border="1pt solid black">
<fo:block>
<xsl:value-of select="."/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="names">
<xsl:choose>
<xsl:when test="number(./name/position()) = 1">
<fo:table-row>
<fo:table-cell column-number="1" border="1pt solid black">
<fo:block> Name </fo:block>
</fo:table-cell>
<fo:table-cell column-number="2" border="1pt solid black">
<fo:block>
<xsl:value-of select="./name/#language"/>
</fo:block>
</fo:table-cell>
<fo:table-cell column-number="3" border="1pt solid black">
<fo:block>
<xsl:value-of select="./name"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:when>
<xsl:otherwise>
<fo:table-row>
<fo:table-cell column-number="1" border="1pt solid black">
<fo:block> NameXXX </fo:block>
</fo:table-cell>
<fo:table-cell column-number="2" border="1pt solid black">
<fo:block>
<xsl:value-of select="./name/#language"/>
</fo:block>
</fo:table-cell>
<fo:table-cell column-number="3" border="1pt solid black">
<fo:block>
<xsl:value-of select="./name"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="p">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="b">
<fo:inline font-weight="bold">
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
</xsl:stylesheet>
My question is about the names element for different languages.
So the first Line in the table of the names element looks like this:
-----------------------------
|Name | DE | Biersorte |
-----------------------------
But I`m missing the second line:
-----------------------------
| | EN | beer type |
-----------------------------
What am I doing wrong with the xsl:choose->when->otherwise function?
I would be happy about a tip.
The relevant code is in a template matching names, but there is only one such element in your XSLT and so it will only be called once.
The expression number(./name/position()) = 1 in this context is simply asking "For this names element, is there a name element in position 1", which is true, and so the xsl:when gets executed.
Your code really needs to be in a block where name is selected. Try this XSLT (which also removes the code duplication).
<xsl:template match="names">
<xsl:for-each select="name">
<fo:table-row>
<fo:table-cell column-number="1" border="1pt solid black">
<fo:block>
<xsl:if test="position() = 1">Name</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell column-number="2" border="1pt solid black">
<fo:block>
<xsl:value-of select="#language"/>
</fo:block>
</fo:table-cell>
<fo:table-cell column-number="3" border="1pt solid black">
<fo:block>
<xsl:value-of select="."/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</xsl:template>
I'm trying to render this list as a table in xsl-fo
<ul class="tablelist">
<li class="a">A</li>
<li class="a">A
</li>
<li class="b">B
</li>
<li class="b">B
</li>
<li class="a">A</li>
<li class="b">B</li>
<li class="a">A</li>
</ul>
All the ones with class A in the left column, all the ones with class B in the right column.
My current solution:
<fo:table>
<fo:table-column column-number="1" column-width="30mm"/>
<fo:table-column column-number="2" />
<fo:table-body>
<xsl:for-each select="li[#class='a']">
<fo:table-row>
<fo:table-cell column-number="1">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
<xsl:for-each select="li[#class='b']">
<fo:table-row>
<fo:table-cell column-number="2">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
... doesnt work, of course. What would I need to change?
Thanks for help!
EDIT:
Desired output in this case would be:
<fo:table>
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:block>
A
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>
B
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:block>
A
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>
B
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:block>
A
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>
B
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell>
<fo:block>
A
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block/
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
because there are four class="a" and three class="b"elements. So, 4 rows in total, in 4 rows the left cell is A and in three of those rows, the right column is B.
Hope, it is a bit clearer now!
Here's another way you could look at it:
XSLT 1.0
<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:template match="/">
<table>
<xsl:call-template name="generate-rows">
<xsl:with-param name="left" select="ul/li[#class='a']"/>
<xsl:with-param name="right" select="ul/li[#class='b']"/>
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="generate-rows">
<xsl:param name="left"/>
<xsl:param name="right"/>
<xsl:param name="i" select="1"/>
<xsl:if test="$i <= count($left) or $i <= count($right)">
<row>
<cell><xsl:value-of select="$left[$i]"/></cell>
<cell><xsl:value-of select="$right[$i]"/></cell>
</row>
<xsl:call-template name="generate-rows">
<xsl:with-param name="left" select="$left"/>
<xsl:with-param name="right" select="$right"/>
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
One option is to use position() to apply-templates to the element with the opposite class that's in the same position.
If you know that there will always be more a class elements than b class elements (or if you don't care what happens to the remaining b class elements), you could do something like this:
<xsl:template match="ul[#class='tablelist']">
<fo:table>
<fo:table-column column-number="1" column-width="30mm"/>
<fo:table-column column-number="2" />
<fo:table-body>
<xsl:for-each select="li[#class='a']">
<xsl:variable name="pos" select="position()"/>
<fo:table-row>
<fo:table-cell column-number="1">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
<fo:table-cell column-number="2">
<fo:block>
<xsl:apply-templates select="../li[#class='b'][position()=$pos]"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</xsl:template>
If you do care about those b class elements, when there are more b than a, you could do something like this:
<xsl:template match="ul[#class='tablelist']">
<fo:table>
<fo:table-column column-number="1" column-width="30mm"/>
<fo:table-column column-number="2" />
<fo:table-body>
<xsl:choose>
<xsl:when test="count(li[#class='a']) >= count(li[#class='a'])">
<xsl:apply-templates select="li[#class='a']" mode="createRow"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="li[#class='b']" mode="createRow"/>
</xsl:otherwise>
</xsl:choose>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="li[#class='a']" mode="createRow">
<xsl:variable name="pos" select="position()"/>
<fo:table-row>
<fo:table-cell column-number="1">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
<fo:table-cell column-number="2">
<fo:block>
<xsl:apply-templates select="../li[#class='b'][position()=$pos]"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
<xsl:template match="li[#class='b']" mode="createRow">
<xsl:variable name="pos" select="position()"/>
<fo:table-row>
<fo:table-cell column-number="1">
<fo:block>
<xsl:apply-templates select="../li[#class='a'][position()=$pos]"/>
</fo:block>
</fo:table-cell>
<fo:table-cell column-number="2">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
why does the table cell disappear when there is no photo existing to generate?
i am using the following code which is not working. What do i need to change if i want to generate a blank table cell if there is no photo existing to generate?
<xsl:for-each select="...............">
<xsl:choose>
<xsl:when test="*">
<xsl:if test=".....">
<xsl:if test=".......">
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:external-graphic src="url('{concat($FILEPATH,.....'])}')"
inline-progression-dimension.maximum="4.1cm" block-progression-dimension.maximum="4cm"
content-width="scale-to-fit" content-height= "scale-to-fit" scaling="uniform"/>
</fo:block>
</fo:table-cell>
</xsl:if>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
Your have <xsl:when test="*"> ... is that node empty? If that is your outside test and passes, but the other IFs (you do not show) do not pass their test, your template yields nothing.
Breaking this down with comments:
<xsl:when test="*">
<xsl:if test=".....">
<!-- If this does not pass, you get nothing -->
<xsl:if test=".......">
<!-- If this does not pass, you get nothing -->
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:external-graphic src="url('{concat($FILEPATH,.....'])}')"
inline-progression-dimension.maximum="4.1cm" block-progression-dimension.maximum="4cm"
content-width="scale-to-fit" content-height= "scale-to-fit" scaling="uniform"/>
</fo:block>
</fo:table-cell>
</xsl:if>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
Kevin is right, given your example, your when clause is likely satisfied but if one of your if statements evaluates as false, you will get an empty table-cell. My advice is to add the conditions of your if statements to the when clause using logic operators such as AND/OR, for example, say the conditions in your template where like this...
<xsl:when test="$node = 'A'">
<xsl:if test="$node/child = 'B'">
<xsl:if test="not(contains($node/child,'C'))">
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
...
</fo:table-cell>
</xsl:if>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
Could be expressed as
<xsl:when test="$node = 'A' AND $node/child = 'B' AND not(contains($node/child,'C'))">
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
...
</fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell border="solid" text-align="center" font-weight="bold" number-columns-spanned="1">
<fo:block>
<fo:leader/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
And in doing so, you will make sure that if any of those three logical conditions aren't met, that your otherwise block will be called and the leader should keep the cell from collapsing, rather than the when statement being called despite the fact that your logic conditions aren't technically satisfied, and winding up with an empty cell which will be collapsed by FOP by default. Hope this clears things up a bit for you.
I am trying to format a report and I have tables that will span multiple pages with various items/fees, and what i would like to do ideally is to display a running total for each page in the table footer, basically i want the sum of the values in my table from the current page back to the first page to display at the bottom. Is there any way to achieve this?
<fo:table width="100%" border-style="groove" border-width="2pt" background-repeat="repeat">
<fo:table-column column-width="5%" />
<fo:table-column column-width="60%" />
<fo:table-column column-width="5%" />
<fo:table-column column-width="7.5%" />
<fo:table-column column-width="7.5%" />
<fo:table-column column-width="7.5%" />
<fo:table-column column-width="7.5%" />
<fo:table-footer border-top-style="dashed" border-bottom-style="dashed">
<fo:table-cell display-align="center">
</fo:table-cell>
<fo:table-cell display-align="center">
<fo:block text-align="center">
Page <fo:page-number/>
<xsl:text> OF </xsl:text>
<fo:page-number-citation ref-id="end" />
</fo:block>
</fo:table-cell>
<fo:table-cell display-align="center">
</fo:table-cell>
<fo:table-cell display-align="center">
</fo:table-cell>
<fo:table-cell display-align="center">
</fo:table-cell>
<fo:table-cell display-align="center">
<fo:block text-align="end">
Page Total:
</fo:block>
</fo:table-cell>
<fo:table-cell display-align="center">
</fo:table-cell>
</fo:table-footer>
<fo:table-body>
<xsl:for-each select="/receipt_invoice/details/product_lot">
<xsl:variable name="untitled" select="." />
<fo:table-row>
<fo:table-cell border-style="solid" border-width="1pt" border-color="black" padding="2pt" background-repeat="repeat" display-align="center">
<fo:block text-align="right">
<fo:block>
<xsl:value-of select="lot/quantity" />
</fo:block>
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="1pt" border-color="black" padding="2pt" background-repeat="repeat" display-align="center">
<fo:block>
<fo:table width="100%" border-style="none" border-width="2pt" background-repeat="repeat">
<fo:table-column/>
<fo:table-column/>
<fo:table-body>
<fo:table-row>
<fo:table-cell border-style="none" border-width="2pt" padding="2pt" background-repeat="repeat" display-align="before">
<fo:block>
<fo:block>
<xsl:value-of select="product/code" />
</fo:block>
<fo:block>
Lot Qty:
<xsl:value-of select="lot/quantity" />
</fo:block>
<fo:block>
<xsl:text>
</xsl:text>
</fo:block>
<fo:block>
<xsl:value-of select="lot/identifier" />
</fo:block>
<fo:block>
<xsl:value-of select="lot/lot_components/component/label" />
</fo:block>
<fo:block>
<xsl:value-of select="lot/lot_components/component/value" />
</fo:block>
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="none" border-width="2pt" padding="2pt" background-repeat="repeat" display-align="before">
<fo:block>
<fo:block>
<xsl:value-of select="product/first_description" />
</fo:block>
<fo:block>
<xsl:value-of select="lot/csd/csd_line" />
</fo:block>
<fo:block>
<xsl:value-of select="lot/csd/set_description" />
</fo:block>
<fo:block>
<xsl:value-of select="lot/lot_components/component/label" />
</fo:block>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
Line Number:
<xsl:value-of select="line_number" />
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="1pt" border-color="black" padding="2pt" background-repeat="repeat" display-align="center">
<fo:block text-align="center">
<fo:block>
<xsl:value-of select="product/unit_of_measure" />
</fo:block>
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="1pt" border-color="black" padding="2pt" background-repeat="repeat" display-align="center">
<fo:block text-align="center">
<fo:block>
<xsl:value-of select="stock_charges/storage_rate" />
</fo:block>
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="1pt" border-color="black" padding="2pt" background-repeat="repeat" display-align="center">
<fo:block text-align="center">
<fo:block>
<xsl:value-of select="stock_charges/storage_amount" />
</fo:block>
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="1pt" border-color="black" padding="2pt" background-repeat="repeat" display-align="center">
<fo:block text-align="center">
<fo:block>
<xsl:value-of select="stock_charges/handling_rate" />
</fo:block>
</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" border-width="1pt" border-color="black" padding="2pt" background-repeat="repeat" display-align="center">
<fo:block text-align="center">
<fo:block>
<xsl:value-of select="stock_charges/handling_amount" />
</fo:block>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
Revision:
So I have decided to use a page footer to display the subtotal on each page, and have set up a marker in my table that simply returns the grand total just to test it out, but now what i was think was that i could try to somehow get the position in my xml file the node was taken from and use something like...
<xsl:value-of select="sum(preceding::value[the_position]"/>
Where value would actually be the name of the charge being totaled up, is this something that could work? If have been trying different things but I am uncertain of the syntax and how to return the position.
For completeness, posting the description of the solution as an answer.
If your rows are of pretty regular height, you can use sum() function over all previous nodes in your table structure as you output rows and put that running subtotal as a table/table row inside a marker for the footer of the page as a single row table. Retrieve the marker into the page footer (not the table-footer with retrieve-table-marker) using the last one on the page, then clear the marker. Careful sizing and you can get it to look just like it is a table footer and you can clear that marker at the end of the table so that it does not appear on other pages. For the table end, just output the totals.
It may not work with complex tables, especially where there are multi-line rows and keeps on those rows as the table may not reach the footer.
This overcomes two issues (1) you do not need table-markers as many FO engines do not support them (actually there are good reasons for this as they are much more problematic than you think if the content being retrieved could have a large, variable height). And (2) using table-markers can run into issues at the end of the table inside that footer where you may not want it (although you can clear the marker at the last row).
I'm not sure if this is even possible but here we go.
I have an xml file:
<ROOT>
<MEM-STMT>
<COUNTRY>co</COUNTRY>
<CURRENCY>cu</CURRENCY>
<STMT>
<ST-NO>1</ST-NO>
<ST-DATE>21-JUL-11</ST-DATE>
<CC>21</CC>
<YY>11</YY>
<MM>07</MM>
<DD>21</DD>
</STMT>
<MEM-DET>
<MEM-NO>9</MEM-NO>
<MEM-PROD-LEV>24</MEM-PROD-LEV>
<MEM-OPTION>option</MEM-OPTION>
<MEM-EMP-NO/>
<MEM-EMP-NAME>name</MEM-EMP-NAME>
</MEM-DET>
<MEM-ADDR>
<MEM-NAME>name</MEM-NAME>
<MEM-ADDR1> Addr1</MEM-ADDR1>
<MEM-ADDR2> Addr2</MEM-ADDR2>
<MEM-ADDR3> Addr3</MEM-ADDR3>
<MEM-SUB> Sub</MEM-SUB>
<MEM-CITY> +2</MEM-CITY>
<MEM-REG> Employer:reg</MEM-REG>
<MEM-CNTRY/>
<MEM-PCODE/>
<MEM-EMPCODE/>
</MEM-ADDR>
<ACC>
<PROV>
<PR-NO>1</PR-NO>
<PR-NAME> pr</PR-NAME>
<REF-NO>1</REF-NO>
<CLM>
<REF>1</REF>
<CL-DEP-NO>04</CL-DEP-NO>
<CL-DEP-NAME>dep</CL-DEP-NAME>
<CL-DOS>10-APR-11</CL-DOS>
<CC>21</CC>
<YY>11</YY>
<MM>04</MM>
<DD>10</DD>
<CL-TAR-DRUG-CD>1 </CL-TAR-DRUG-CD>
<CL-TAR-DRUG-DESC>desc </CL-TAR-DRUG-DESC>
<CL-NO>1</CL-NO>
<CL-LINE>2</CL-LINE>
<CL-AMT>16.8</CL-AMT>
<CL-TAR-AMT>16.8</CL-TAR-AMT>
<CL-PAID-PROV>16.8</CL-PAID-PROV>
<CL-PAID-MEM>0</CL-PAID-MEM>
<CL-RSN/>
</CLM>
<CLM>
<REF>1</REF>
<CL-DEP-NO>04</CL-DEP-NO>
<CL-DEP-NAME>dep </CL-DEP-NAME>
<CL-DOS>20-APR-11</CL-DOS>
<CC>21</CC>
<YY>11</YY>
<MM>04</MM>
<DD>20</DD>
<CL-TAR-DRUG-CD>1 </CL-TAR-DRUG-CD>
<CL-TAR-DRUG-DESC>desc </CL-TAR-DRUG-DESC>
<CL-NO>1</CL-NO>
<CL-LINE>2</CL-LINE>
<CL-AMT>50.4</CL-AMT>
<CL-TAR-AMT>50.4</CL-TAR-AMT>
<CL-PAID-PROV>50.4</CL-PAID-PROV>
<CL-PAID-MEM>0</CL-PAID-MEM>
<CL-RSN/>
</CLM>
<TOTALS>
<TOT-AMT>67.2</TOT-AMT>
<TOT-TAR-AMT>67.2</TOT-TAR-AMT>
<TOT-PAID-PROV>67.2</TOT-PAID-PROV>
<TOT-PAID-MEM>0</TOT-PAID-MEM>
</TOTALS>
</PROV>
<PROV>
<PR-NO>2</PR-NO>
<PR-NAME> pr</PR-NAME>
<REF-NO>1</REF-NO>
<CLM>
<REF>1</REF>
<CL-DEP-NO>04</CL-DEP-NO>
<CL-DEP-NAME>dep</CL-DEP-NAME>
<CL-DOS>10-APR-11</CL-DOS>
<CC>21</CC>
<YY>11</YY>
<MM>04</MM>
<DD>10</DD>
<CL-TAR-DRUG-CD>1</CL-TAR-DRUG-CD>
<CL-TAR-DRUG-DESC>desc </CL-TAR-DRUG-DESC>
<CL-NO>1</CL-NO>
<CL-LINE>2</CL-LINE>
<CL-AMT>15</CL-AMT>
<CL-TAR-AMT>0</CL-TAR-AMT>
<CL-PAID-PROV>15</CL-PAID-PROV>
<CL-PAID-MEM>0</CL-PAID-MEM>
<CL-RSN/>
</CLM>
<TOTALS>
<TOT-AMT>15</TOT-AMT>
<TOT-TAR-AMT>0</TOT-TAR-AMT>
<TOT-PAID-PROV>15</TOT-PAID-PROV>
<TOT-PAID-MEM>0</TOT-PAID-MEM>
</TOTALS>
</PROV>
<PROV>
<PR-NO>1</PR-NO>
<PR-NAME> pr</PR-NAME>
<REF-NO>I0428202</REF-NO>
<CLM>
<REF>I0428202</REF>
<CL-DEP-NO>03</CL-DEP-NO>
<CL-DEP-NAME>dep</CL-DEP-NAME>
<CL-DOS>10-APR-11</CL-DOS>
<CC>21</CC>
<YY>11</YY>
<MM>04</MM>
<DD>10</DD>
<CL-TAR-DRUG-CD>2</CL-TAR-DRUG-CD>
<CL-TAR-DRUG-DESC>desc</CL-TAR-DRUG-DESC>
<CL-NO>112153</CL-NO>
<CL-LINE>217615</CL-LINE>
<CL-AMT>31.58</CL-AMT>
<CL-TAR-AMT>0</CL-TAR-AMT>
<CL-PAID-PROV>31.58</CL-PAID-PROV>
<CL-PAID-MEM>0</CL-PAID-MEM>
<CL-RSN/>
</CLM>
<TOTALS>
<TOT-AMT>31.58</TOT-AMT>
<TOT-TAR-AMT>0</TOT-TAR-AMT>
<TOT-PAID-PROV>31.58</TOT-PAID-PROV>
<TOT-PAID-MEM>0</TOT-PAID-MEM>
</TOTALS>
</PROV>
<PROV>
<PR-NO>1</PR-NO>
<PR-NAME> pr</PR-NAME>
<REF-NO>0027579</REF-NO>
<CLM>
<REF>1</REF>
<CL-DEP-NO>04</CL-DEP-NO>
<CL-DEP-NAME>dep</CL-DEP-NAME>
<CL-DOS>09-JUN-11</CL-DOS>
<CC>21</CC>
<YY>11</YY>
<MM>06</MM>
<DD>09</DD>
<CL-TAR-DRUG-CD>99200 </CL-TAR-DRUG-CD>
<CL-TAR-DRUG-DESC>desc</CL-TAR-DRUG-DESC>
<CL-NO>1</CL-NO>
<CL-LINE>1</CL-LINE>
<CL-AMT>12</CL-AMT>
<CL-TAR-AMT>0</CL-TAR-AMT>
<CL-PAID-PROV>12</CL-PAID-PROV>
<CL-PAID-MEM>0</CL-PAID-MEM>
<CL-RSN/>
</CLM>
<TOTALS>
<TOT-AMT>12</TOT-AMT>
<TOT-TAR-AMT>0</TOT-TAR-AMT>
<TOT-PAID-PROV>12</TOT-PAID-PROV>
<TOT-PAID-MEM>0</TOT-PAID-MEM>
</TOTALS>
</PROV>
</ACC>
<RSN-LIST>
<RSN-ERR>????</RSN-ERR>
<RSN-DESC/>
</RSN-LIST>
<FIN-OTHER>
<FIN-DATE/>
<FIN-AMT>0</FIN-AMT>
<FIN-TT/>
</FIN-OTHER>
<GTOTALS>
<GTOT-AMT>125.78</GTOT-AMT>
<GTOT-TAR-AMT>67.2</GTOT-TAR-AMT>
<GTOT-PAID-PROV>125.78</GTOT-PAID-PROV>
<GTOT-PAID-MEM>0</GTOT-PAID-MEM>
</GTOTALS>
<MEM-GTOT>
<M-MESSAGE/>
<M-TOT-PAID-MEM>0</M-TOT-PAID-MEM>
<M-PMT-METH/>
</MEM-GTOT>
<FIN-BAL>
<FIN-BDATE>21-JUL-11</FIN-BDATE>
<FIN-BAMT>0</FIN-BAMT>
<FIN-BTT/>
</FIN-BAL>
</MEM-STMT>
</ROOT>
& I'd like to split the records returned from it using the logic below:
If the xml file has 30 or more PROV-or-CLM-or-TOTATALS nodes then process only 30 PROV-or-CLM-or-TOTATALS nodes. Do this for the entire xml file in case where the number of remaining nodes is less than 30 process them to complete the transformation.
I'm using the following xsl to do this:
<xsl:template name="PROVIDER">
<fo:page-sequence master-reference="global">
<fo:flow flow-name="xsl-region-body">
<fo:wrapper font-size="7pt" font-family="Helvetica">
<fo:block-container >
<xsl:variable name="CURRENCYSYMBOL"><xsl:value-of select="ROOT/MEM-STMT/CURRENCY"/></xsl:variable>
<xsl:for-each select="ROOT/MEM-STMT/ACC/PROV">
<xsl:if test="(position() mod 30 = 1)">
<fo:block >
<fo:table table-layout="fixed" width="190mm" border-style="solid">
<fo:table-column column-width="22mm"/>
<fo:table-column column-width="16mm"/>
<fo:table-column column-width="18mm"/>
<fo:table-column column-width="39mm"/>
<!-- <fo:table-column column-width="15mm"/> -->
<fo:table-column column-width="20mm"/>
<fo:table-column column-width="20mm"/>
<fo:table-column column-width="20mm"/>
<fo:table-column column-width="20mm"/>
<fo:table-column column-width="15mm"/>
<fo:table-header border="solid" >
<fo:table-row border-style="solid">
<fo:table-cell background-color="{$HeaderGray}" number-columns-spanned="9" padding="2pt">
<fo:block font-size="7pt" text-align="left" font-weight="bold">Provider:
<xsl:value-of select="PR-NO"/>-
<xsl:value-of select="PR-NAME"/>
<xsl:text> </xsl:text> Ref No:
<xsl:value-of select="REF-NO"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row border-style="solid">
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Patient</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Serv Date</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Tariff/Drug</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Description</fo:block>
</fo:table-cell>
<!-- <fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Claim No</fo:block>
</fo:table-cell> -->
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Claim Amount</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Tariff Amount</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Paid Provider</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Paid Member</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Reason</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<xsl:for-each select="./CLM">
<xsl:if test="(position() mod 30 = 1)">
<fo:table-body font-size="7pt">
<fo:table-row>
<fo:table-cell padding="2pt" >
<fo:block>
<xsl:value-of select="CL-DEP-NAME"/><xsl:text> </xsl:text>
<xsl:value-of select="CL-DEP-NO"/>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt" >
<fo:block>
<xsl:value-of select="CL-DOS"/>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt" >
<fo:block>
<xsl:value-of select="CL-TAR-DRUG-CD"/>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt" >
<fo:block>
<xsl:value-of select="CL-TAR-DRUG-DESC"/>
</fo:block>
</fo:table-cell>
<!-- <fo:table-cell padding="2pt">
<fo:block>
<xsl:value-of select="CL-NO"/>
</fo:block>
</fo:table-cell> -->
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-AMT != '0'">
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of select="format-number(CL-AMT,'#,##0.00')"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-TAR-AMT != '0'">
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of select="format-number(CL-TAR-AMT,'#,##0.00')"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt" >
<fo:block text-align="right">
<xsl:if test="CL-PAID-PROV != '0'">
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of select="format-number(CL-PAID-PROV,'#,##0.00')"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-PAID-MEM != '0'">
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of select="format-number(CL-PAID-MEM,'#,##0.00')"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-RSN != '????'">
<xsl:value-of select="CL-RSN"/>
</xsl:if>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="./TOTALS">
<xsl:if test="(position() mod 30 = 1)">
<fo:table-body font-size="7pt">
<fo:table-row border-style="solid">
<fo:table-cell padding="2pt" number-columns-spanned="4" >
<fo:block >Totals for Invoice</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt" >
<fo:block text-align="right">
<!-- <xsl:if test="TOT-AMT != '0'"> -->
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of select="format-number(TOT-AMT,'#,##0.00')"/>
<!-- </xsl:if> -->
</fo:block>
</fo:table-cell >
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<!-- <xsl:if test="TOT-TAR-AMT != '0'"> -->
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of select="format-number(TOT-TAR-AMT,'#,##0.00')"/>
<!-- </xsl:if> -->
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<!-- <xsl:if test="TOT-PAID-PROV != '0'"> -->
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of select="format-number(TOT-PAID-PROV,'#,##0.00')"/>
<!-- </xsl:if> -->
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<!-- <xsl:if test="TOT-PAID-MEM != '0'"> -->
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of select="format-number(TOT-PAID-MEM,'#,##0.00')"/>
<!-- </xsl:if> -->
</fo:block>
</fo:table-cell>
<fo:table-cell >
<fo:block>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</xsl:if>
</xsl:for-each>
</fo:table>
</fo:block>
</xsl:if>
</xsl:for-each>
</fo:block-container>
</fo:wrapper>
</fo:flow>
</fo:page-sequence>
My current scenario is such that the split doesn't work & I get the exception: Exception in thread "AWT-EventQueue-2" java.lang.OutOfMemoryError: Java heap space despite allocating -Xms/-Xmx2G memory.
Kamza, you had part of it right, but there are a few flaws in your approach. You start with a for-each on PROV elements, and within the for-each you have an if that causes output to be written only for PROV elements 1, 31, 61, etc. The nested for-each loops have a similar problem. Only CLM element 1, 31, 61, .. is written, like only TOTALS 1, 31, 61, ..
(Actually, I suspect there will only be one TOTALS within each PROV, so that would ok in your code.)
Furthermore, from what I understand from your description and the comments is that you are trying to write out 30 rows at a time, and start a new table, or even a new page after that, with another 30 rows, or what is left. Since no rows are written for the PROV element itself, I presume that it is just about CLM and TOTALS rows, where the count of 30 should include both.
What you need for that is an outer for-each loop that walks along all CLM and TOTALS elements. You can use this expression for that:
<xsl:for-each select="ROOT/MEM-STMT/ACC/PROV/*[self::CLM or self::TOTALS]">
Then use the if you already have to create a new table for row 1, 31, 61, ...
To fill the table with the first-next 30 items, you will need to look forward 30 positions. The PROV element is a bit of a problem here. Assuming CLM and TOTALS only occur within PROV, and PROV is not used elsewhere, you can reasonably safely use the following axis for that:
<xsl:for-each select="(following::*[self::CLM or self::TOTALS])[30 >= position()]">
That returns 30 items, both CLM and TOTALS mixed, but in document order. You need two extra if's (or a choose) to distinguish between these two, and output the appropriate table row:
<xsl:if test="self::CLM">
...
</xsl:if>
<xsl:if test="self::TOTALS">
...
</xsl:if>
In your code you have fo:table-body within the inner for-each loops, causing each table-row to be wrapped in a new table-body. That is unnecessary. Move the table-body to outside the inner loops. You would need only one wrapping both rows of CLM and TOTALS.
Finally, you write a new table for each 30th item. I think you want to start a new page-sequence as well. That will cause each table to be shown on a separate page. Putting things together, you get an XSLT similar to this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:variable name="HeaderGray" select="'gray'"/>
<xsl:template match="/">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="global">
<fo:region-body/>
</fo:simple-page-master>
</fo:layout-master-set>
<xsl:apply-templates select="/" mode="PROVIDER"/>
</fo:root>
</xsl:template>
<xsl:template match="/" mode="PROVIDER">
<xsl:variable name="CURRENCYSYMBOL" select="string(ROOT/MEM-STMT/CURRENCY)"/>
<xsl:for-each select="ROOT/MEM-STMT/ACC/PROV/*[self::CLM or self::TOTALS]">
<xsl:if test="position() mod 30 = 1">
<fo:page-sequence master-reference="global">
<fo:flow flow-name="xsl-region-body">
<fo:wrapper font-size="7pt" font-family="Helvetica">
<fo:block-container>
<fo:block>
<fo:table table-layout="fixed" width="190mm"
border-style="solid">
<fo:table-column column-width="22mm"/>
<fo:table-column column-width="16mm"/>
<fo:table-column column-width="18mm"/>
<fo:table-column column-width="39mm"/>
<!-- <fo:table-column column-width="15mm"/> -->
<fo:table-column column-width="20mm"/>
<fo:table-column column-width="20mm"/>
<fo:table-column column-width="20mm"/>
<fo:table-column column-width="20mm"/>
<fo:table-column column-width="15mm"/>
<fo:table-header border="solid">
<fo:table-row border-style="solid">
<fo:table-cell background-color="{$HeaderGray}"
number-columns-spanned="9" padding="2pt">
<fo:block font-size="7pt" text-align="left"
font-weight="bold">Provider: <xsl:value-of
select="PR-NO"/>- <xsl:value-of select="PR-NAME"/>
<xsl:text> </xsl:text> Ref No:
<xsl:value-of select="REF-NO"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row border-style="solid">
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Patient</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Serv Date</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Tariff/Drug</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Description</fo:block>
</fo:table-cell>
<!-- <fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold">Claim No</fo:block>
</fo:table-cell> -->
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Claim Amount</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Tariff Amount</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Paid Provider</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Paid Member</fo:block>
</fo:table-cell>
<fo:table-cell border-style="solid" padding="2pt">
<fo:block text-align="center" font-weight="bold"
>Reason</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body font-size="7pt">
<xsl:for-each
select="(following::*[self::CLM or self::TOTALS])[30 >= position()]">
<xsl:if test="self::CLM">
<fo:table-row>
<fo:table-cell padding="2pt">
<fo:block>
<xsl:value-of select="CL-DEP-NAME"/>
<xsl:text> </xsl:text>
<xsl:value-of select="CL-DEP-NO"/>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block>
<xsl:value-of select="CL-DOS"/>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block>
<xsl:value-of select="CL-TAR-DRUG-CD"/>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block>
<xsl:value-of select="CL-TAR-DRUG-DESC"/>
</fo:block>
</fo:table-cell>
<!-- <fo:table-cell padding="2pt">
<fo:block>
<xsl:value-of select="CL-NO"/>
</fo:block>
</fo:table-cell> -->
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-AMT != '0'">
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of
select="format-number(CL-AMT,'#,##0.00')"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-TAR-AMT != '0'">
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of
select="format-number(CL-TAR-AMT,'#,##0.00')"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-PAID-PROV != '0'">
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of
select="format-number(CL-PAID-PROV,'#,##0.00')"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-PAID-MEM != '0'">
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of
select="format-number(CL-PAID-MEM,'#,##0.00')"/>
</xsl:if>
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<xsl:if test="CL-RSN != '????'">
<xsl:value-of select="CL-RSN"/>
</xsl:if>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:if>
<xsl:if test="self::TOTALS">
<fo:table-row border-style="solid">
<fo:table-cell padding="2pt"
number-columns-spanned="4">
<fo:block>Totals for Invoice</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<!-- <xsl:if test="TOT-AMT != '0'"> -->
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of
select="format-number(TOT-AMT,'#,##0.00')"/>
<!-- </xsl:if> -->
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<!-- <xsl:if test="TOT-TAR-AMT != '0'"> -->
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of
select="format-number(TOT-TAR-AMT,'#,##0.00')"/>
<!-- </xsl:if> -->
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<!-- <xsl:if test="TOT-PAID-PROV != '0'"> -->
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of
select="format-number(TOT-PAID-PROV,'#,##0.00')"/>
<!-- </xsl:if> -->
</fo:block>
</fo:table-cell>
<fo:table-cell padding="2pt">
<fo:block text-align="right">
<!-- <xsl:if test="TOT-PAID-MEM != '0'"> -->
<xsl:value-of select="$CURRENCYSYMBOL"/>
<xsl:value-of
select="format-number(TOT-PAID-MEM,'#,##0.00')"/>
<!-- </xsl:if> -->
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block> </fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:if>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:block>
</fo:block-container>
</fo:wrapper>
</fo:flow>
</fo:page-sequence>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note: I am not entirely convinced this will prevent you from getting an exception. You gave the impression it is because of your for-each loops, but it may be caused by something else. A table that is simply to big, or an fo document that is too large to render might be the cause as well, for instance. The above code should be fairly efficient. If it is still giving you trouble, try processing things from the command-line, perhaps even using different parsers.
hm.. I don't have time to look at your xslt in detail, but your logic should be possible using something like this:
<xsl:template match="(//PROV | //CLM | //TOTALS)[position() <= 30]">
<xsl:if test="local-name() = 'PROV'">
<!-- do stuff with PROV node -->
</xsl:if>
<xsl:if test="local-name() = 'CLM'">
<!-- do stuff with CLM node -->
</xsl:if>
<xsl:if test="local-name() = 'TOTALS'">
<!-- do stuff with TOTALS node -->
</xsl:if>
</xsl:template>
You could modify your xsl:for-each statements, to restrict them to the first 30 items:
<xsl:for-each select="ROOT/MEM-STMT/ACC/PROV[position()<=30]">
Or you could extract the xsl:for-each logic out into templates and then select only the first 30 to apply templates to:
<xsl:apply-templates select="ROOT/MEM-STMT/ACC/PROV[position()<=30]"/>