Change order of list - xslt

Current List
CurrentList
Access Autocad Burger
Layout Photoshop Sandwich
VisualStudio
I have used a boxed view and edited source like below to get three columns
<xsl:when test="$Position mod 3 = 0">
But the order of items needed is like this
Access Layout VisualStudio
Autocad Photoshop
Burger Sandwich
Can someone suggest a way to achieve this?
This is part of xml
<xsl:template name="NewTRJumbo" ddwrt:ghost="">
<xsl:param name="Position" select="1"/>
<xsl:param name="Collapse" select="."/>
<xsl:choose>
<xsl:when test="$Position mod 3 = 0">
<xsl:text disable-output-escaping="yes"></tr></xsl:text>
<xsl:call-template name="NewTR">
<xsl:with-param name="Collapse" select="$Collapse"/>
<xsl:with-param name="EmptyLine" select="1"/>
</xsl:call-template>
<xsl:text disable-output-escaping="yes"><td></td></tr></xsl:text>
<xsl:call-template name="NewTR">
<xsl:with-param name="Collapse" select="$Collapse"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<td width="1.5%">
</td>
</xsl:otherwise>
</xsl:choose>

I am assuming you have an XML document of elements, for example, something like this
<items>
<item>Access</item>
<item>Autocad</item>
<item>Burger</item>
<item>Layout</item>
<item>Photoshop</item>
<item>Sandwich</item>
<item>VisualStudio</item>
</items>
And you want to group them into 3 columns, but with the items running top-to-bottom first, rather than left-to-right. You current test of $Position mod 3 = 0 is partially along the right lines, but you are actually starting a new row each here, and so end up going left-to-right.
One thing to mention is your use of this line of code
<xsl:text disable-output-escaping="yes"></tr></xsl:text>
It looks like you are simply iterating over the elements, and trying to output a closing tr tag after every third one. XSLT is a functional language, so you need to approach with a different mind set, and do things differentially than you would in a normal procedural language.
Firstly, you need to find the number of rows you will beed to output
<xsl:param name="columns" select="3"/>
<xsl:variable name="items" select="count(/*/item)"/>
<xsl:variable name="rows" select="ceiling($items div $columns)"/>
i.e The rows is the total number of items divided by the number of columns.
Then, you need to select the first item of each row, which is straight-forward because these will be the first 'n' items in your list (Note the use of the mode because the final XSLT will have two templates matching the item element)
<xsl:apply-templates select="item[position() <= $rows]" mode="row"/>
Within the template matching the starting items, you then need to output the current item, and the following sibling items which are in the row
<xsl:apply-templates select=".|following-sibling::item[position() mod $rows = 0]"/>
The only other thing to consider is adding a blank cell to the end of the some rows. This can be done by calculating the number of remaining items compared to the total items
<xsl:variable name="lastItem" select="(position() + $rows * ($columns - 1) - $items)" />
<xsl:if test="$lastItem > 0">
<td colspan="{ceiling($lastItem div $rows)}"></td>
</xsl:if>
Try the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="columns" select="3"/>
<xsl:variable name="items" select="count(/*/item)"/>
<xsl:variable name="rows" select="ceiling($items div $columns)"/>
<xsl:template match="/*">
<table>
<xsl:apply-templates select="item[position() <= $rows]" mode="row"/>
</table>
</xsl:template>
<xsl:template match="item" mode="row">
<tr>
<xsl:apply-templates select=".|following-sibling::item[position() mod $rows = 0]"/>
<xsl:variable name="lastItem" select="(position() + $rows * ($columns - 1) - $items)" />
<xsl:if test="$lastItem > 0">
<td colspan="{ceiling($lastItem div $rows)}"></td>
</xsl:if>
</tr>
</xsl:template>
<xsl:template match="item">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
When applied to the XML shown at the beginning of the question, the following is output
<table>
<tr>
<td>Access</td>
<td>Layout</td>
<td>VisualStudio</td>
</tr>
<tr>
<td>Autocad</td>
<td>Photoshop</td>
<td colspan="1"></td>
</tr>
<tr>
<td>Burger</td>
<td>Sandwich</td>
<td colspan="1"></td>
</tr>
</table>

Related

Adding data from one node to another in an XML doc using XSLT

I have an XML doc with two sets of data in it. I need to output a HTML table that combines the data.
The XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<fdata>
<RepairHistory>
<RepairList>
<RepairJob>
<RepairRef>56740408</RepairRef>
<RepairDescription>Baths</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
<RepairJob>
<RepairRef>56735043</RepairRef>
<RepairDescription>Basins</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
<RepairJob>
<RepairRef>56740415</RepairRef>
<RepairDescription>Showers</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
</RepairList>
</RepairHistory>
<map>
<entry>
<string>GUID</string>
<string>be0f53f5-7a09-47cd-9928-0c865b6450a5</string>
</entry>
<entry>
<string>JOBNUMBER</string>
<string>56740408</string>
</entry>
</map>
<map>
<entry>
<string>GUID</string>
<string>5ce2e8fe-7735-4f98-b3a9-3bd386edb338</string>
</entry>
<entry>
<string>JOBNUMBER</string>
<string>56740415</string>
</entry>
</map>
</fdata>
JOBNUMBER has to match RepairRef. Not all of the data at the bottom matches the data at the top.
I need to output the data in a table as follows:
Date Ref Details GUID
20180515 56740408 Baths be0f53f5-7a09-47cd-9928-0c865b6450a5
20180515 56735043 Basins No GUID
20180515 56740415 Showers 5ce2e8fe-7735-4f98-b3a9-3bd386edb338
I have tried to output the table using the following xsl, but it doesn't work...
<xsl:template match="/">
<h3>Repair history</h3>
<table>
<tr>
<th>Date</th>
<th>Ref</th>
<th>Details</th>
<th>GUID</th>
</tr>
<xsl:apply-templates select ="//RepairJob" />
</table>
</xsl:template>
<xsl:template match="RepairJob">
<xsl:variable name="JobNumber" select="RepairRef" />
<tr>
<td><xsl:value-of select="RepairStartDate"/></td>
<td><xsl:value-of select="RepairRef"/></td>
<td><xsl:value-of select="RepairDescription"/></td>
<td>
<xsl:choose>
<xsl:when test="count(//map//entry[2]//string[2] = $JobNumber) > 0">
<xsl:value-of select="//map//entry[2]//string[2] = $JobNumber"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
I can see that I am not selecting the correct node to display, but the matching is not working either. Can anyone help?
Your current xsl:value-of evaluates to a boolean ("true" or "false"). The expression you actually want is this...
<xsl:value-of select="//map[entry[2]/string[2] = $JobNumber]/entry[1]/string[2]"/>
The xsl:when needs a tweak too, so the whole xsl:choose should look like this:
<xsl:choose>
<xsl:when test="//map[entry[2]/string[2] = $JobNumber]">
<xsl:value-of select="//map[entry[2]/string[2] = $JobNumber]/entry[1]/string[2]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
However, consider using a key to look up the guids....
<xsl:key name="map" match="map" use="entry[string[1] = 'JOBNUMBER']/string[2]" />
Then your xsl:value-of becomes this
<xsl:value-of select="key('map',$JobNumber)/entry[string[1] = 'GUID']/string[2]"/>
Note, this also allows for the entry elements to be in any order within a map element (i.e. the JOBNUMBER could be the first entry, and the GUID the second)
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:key name="map" match="map" use="entry[string[1] = 'JOBNUMBER']/string[2]" />
<xsl:template match="/">
<h3>Repair history</h3>
<table>
<tr>
<th>Date</th>
<th>Ref</th>
<th>Details</th>
<th>GUID</th>
</tr>
<xsl:apply-templates select ="//RepairJob" />
</table>
</xsl:template>
<xsl:template match="RepairJob">
<xsl:variable name="JobNumber" select="RepairRef" />
<tr>
<td><xsl:value-of select="RepairStartDate"/></td>
<td><xsl:value-of select="RepairRef"/></td>
<td><xsl:value-of select="RepairDescription"/></td>
<td>
<xsl:choose>
<xsl:when test="key('map',$JobNumber)">
<xsl:value-of select="key('map',$JobNumber)/entry[string[1] = 'GUID']/string[2]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>

Compare xml node values in xsl and highlight differences

I have an xml. I am transforming it using xsl stylesheet and showing in html page using java. My requirement is , I need to compare two node values and if there is a difference, I need to highlight the changed character value. How can this be done ?
XML :
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Message>
<DiffDetailMessage>
<TestCaseID>000001</TestCaseID>
<res1>VI7002 1D</res1>
<res2>VI7002 DD </res2>
</DiffDetailMessage>
<DiffDetailMessage>
<TestCaseID>000002</TestCaseID>
<res1>BS7002 1D</res1>
<res2>BS7002 SS </res2>
</DiffDetailMessage>
</Message>
XSL :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Report</h2>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="Message/DiffDetailMessage">
<table border="2">
<xsl:apply-templates select="TestCaseID"/>
<xsl:apply-templates select="res1"/>
<xsl:apply-templates select="res2"/>
</table>
</xsl:template>
<xsl:template match="TestCaseID">
<tr><td><b>Test CaseID </b></td>
<td><xsl:value-of select="."/></td></tr>
</xsl:template>
<xsl:template match="res1">
<tr><td><b>Res1</b></td>
<td><xsl:value-of select="."/> </td> </tr>
</xsl:template>
<xsl:template match="res2">
<tr><td><b>Res2</b></td>
<td><xsl:value-of select="."/></td> </tr>
</xsl:template>
</xsl:stylesheet>
How can I compare node values res1 with res2 ? In my case, value of "VI70002 ID" should be compared against "VI7002 DD" and since there is a change, I should highlight "D" character in html page using xsl. Can someone help on this regard.
If you are using only XSLT-1.0, you can use a recursive named template to iterate over the string char-by-char: the following template takes two strings as input and puts a bold emphasis on each different char. If the second string is longer than the first one, these trailing chars are highlighted, too.
<xsl:template name="cmp">
<xsl:param name="str1" />
<xsl:param name="str2" />
<xsl:choose>
<xsl:when test="substring($str1,1,1) = substring($str2,1,1)">
<xsl:value-of select="substring($str2,1,1)"/>
</xsl:when>
<xsl:when test="substring($str1,1,1) != substring($str2,1,1)">
<b><xsl:value-of select="substring($str2,1,1)"/></b>
</xsl:when>
<xsl:when test="$str1 = '' and substring($str2,1,1)">
<b><xsl:value-of select="substring($str2,1,1)"/></b>
</xsl:when>
</xsl:choose>
<xsl:if test="string-length($str1) > 0 or string-length($str2) > 0">
<xsl:call-template name="cmp">
<xsl:with-param name="str1" select="substring($str1,2)" />
<xsl:with-param name="str2" select="substring($str2,2)" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Call this template from one of the other templates to get a (partially) highlighted "string", e.g.
<xsl:template match="res2">
<tr><td><b>Res2</b></td>
<td>
<xsl:call-template name="cmp">
<xsl:with-param name="str1" select="../res1" />
<xsl:with-param name="str2" select="." />
</xsl:call-template>
</td> </tr>
</xsl:template>

XSL, SUM & Multiply with Condition

Im doing an assignment for University (so im new to XSL coding) in making a quasi ecommerce site, and will provide as much detail as i can so it makes sense.
Sample XML Data:
<Items>
<Item>
<ItemID>50001</ItemID>
<ItemName>Samsung Galaxy S4</ItemName>
<ItemPrice>629</ItemPrice>
<ItemQty>14</ItemQty>
<ItemDesc>4G Mobile</ItemDesc>
<QtyHold>0</QtyHold>
<QtySold>1</QtySold>
</Item>
<Item>
<ItemID>50002</ItemID>
<ItemName>Samsung Galaxy S5</ItemName>
<ItemPrice>779</ItemPrice>
<ItemQty>21</ItemQty>
<ItemDesc>4G Mobile</ItemDesc>
<QtyHold>0</QtyHold>
<QtySold>1</QtySold>
</Item>
</Items>
Website
So the process is, when a person clicks 'Add to Cart' in the top Table, the ItemQty is decreased by 1 on the ItemQty in the XML, while it increases by 1 in the QtyHold in the XML. (QtyHold represents what has been added to the shopping Cart. Thus if QtyHold is >0 then its been added to the Cart)
My problem refers to the 2nd Table (code below), where the Total figure works - only if dealing with 1 Item. Thus, if Item Number '50001' is added a 2nd time, the Total wont change.
<xsl:template match="/">
<fieldset>
<legend>Shopping Cart</legend>
<BR />
<table border="1" id="CartTable" align="center">
<tr><th>Item Number</th>
<th>Price</th>
<th>Quantity</th>
<th>Remove</th></tr>
<xsl:for-each select="/Items/Item[QtyHold > 0]">
<tr><td><xsl:value-of select="ItemID"/></td>
<td>$<xsl:value-of select="ItemPrice"/></td>
<td><xsl:value-of select="QtyHold"/></td>
<td><button onclick="addtoCart({ItemID}, 'Remove')">Remove from Cart</button></td> </tr>
</xsl:for-each>
<tr><td ALIGN="center" COLSPAN="3">Total:</td><td>$<xsl:value-of select="sum(//Item[QtyHold >0]/ItemPrice)"/></td></tr>
</table>
<BR />
<button onclick="Purchase()" class="submit_btn float_l">Confirm Purchase</button>
<button onclick="CancelOrder()" class="submit_btn float_r">Cancel Order</button>
</fieldset>
</xsl:template>
</xsl:stylesheet>
So what needs to happen is within the following code, while it checks if the QtyHold is greater than 0 (which would mean its in the shopping Cart) & to sum these values, it also needs to multiply QtyHold & ItemPrice.
<xsl:value-of select="sum(//Item[QtyHold >0]/ItemPrice)"/>
I tried many variations of Code like this below... but can't seem to make anything work.
select="sum(//Item[QtyHold >0]/ItemPrice)/(QtyHold*ItemPrice"/>
If you are using XSLT 2.0, the expression you could use would be this:
<xsl:value-of select="sum(//Item[QtyHold >0]/(ItemPrice * QtyHold))"/>
However, in XSLT 1.0 that is not allowed. Instead, you could achieve the result you need with an extension function. In particular the "node-set" function. First you would create a variable like this, in which you construct new nodes holding each item total
<xsl:variable name="itemTotals">
<xsl:for-each select="//Item[QtyHold >0]">
<total>
<xsl:value-of select="ItemPrice * QtyHold" />
</total>
</xsl:for-each>
</xsl:variable>
Ideally, you would like to do sum($itemTotals/total), but this won't work, because itemTotals is a "Result Tree Fragment" and the sum function only accepts a node-set. So you use the node-set extension function to convert it. First declare this namespace in your XSLT...
xmlns:exsl="http://exslt.org/common"
Then, your sum function would look like this:
<xsl:value-of select="sum(exsl:node-set($itemTotals)/total)"/>
Alternatively, if you couldn't even use an extension function, you could use the "following-sibling" approach, to select each Item at a time, and keep a running total. So, you would have a template like this:
<xsl:template match="Item" mode="sum">
<xsl:param name="runningTotal" select="0" />
<xsl:variable name="newTotal" select="$runningTotal + ItemPrice * QtyHold" />
<xsl:variable name="nextItem" select="following-sibling::Item[1]" />
<xsl:choose>
<xsl:when test="$nextItem">
<xsl:apply-templates select="$nextItem" mode="sum">
<xsl:with-param name="runningTotal" select="$newTotal" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTotal" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And to call it, to get the sum, you just start off by selecting the first node
<xsl:apply-templates select="(//Item)[1]" mode="sum" />
Try this XSLT which demonstrates the various approaches
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<table border="1" id="CartTable" align="center">
<tr><th>Item Number</th>
<th>Price</th>
<th>Quantity</th>
</tr>
<xsl:for-each select="/Items/Item[QtyHold > 0]">
<tr>
<td><xsl:value-of select="ItemID"/></td>
<td>$<xsl:value-of select="ItemPrice"/></td>
<td><xsl:value-of select="QtyHold"/></td>
</tr>
</xsl:for-each>
<tr>
<td ALIGN="center" COLSPAN="2">Total:</td>
<xsl:variable name="itemTotals">
<xsl:for-each select="//Item[QtyHold >0]">
<total>
<xsl:value-of select="ItemPrice * QtyHold" />
</total>
</xsl:for-each>
</xsl:variable>
<td>
<!-- XSLT 2.0 only: $<xsl:value-of select="sum(//Item[QtyHold >0]/(ItemPrice * QtyHold))"/>-->
$<xsl:value-of select="sum(exsl:node-set($itemTotals)/total)"/>
$<xsl:apply-templates select="(//Item)[1]" mode="sum" />
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="Item" mode="sum">
<xsl:param name="runningTotal" select="0" />
<xsl:variable name="newTotal" select="$runningTotal + ItemPrice * QtyHold" />
<xsl:variable name="nextItem" select="following-sibling::Item[1]" />
<xsl:choose>
<xsl:when test="$nextItem">
<xsl:apply-templates select="$nextItem" mode="sum">
<xsl:with-param name="runningTotal" select="$newTotal" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTotal" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
As a final thought, why don't you just a new Total element to each Item element in your XML. Initially, it would be set to 0, like QtyHold. Then, when you increment QtyHold by 1, by what ever process you do, you can also increment Total by the amount held in ItemPrice. That way, you can just sum this Total node to get the overall total, without the need for extension functions or recursive templates.

How to get the sum of values in attributes in xslt

I am trying to get the sum. Here is the xslt code.
<xsl:template match="Entry">
<xsl:if test="position() <= 10">
<tr>
<td>
<xsl:value-of select="substring-before(#Value,'||')"/>
</td>
<td>
<xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
</td>
</tr>
</xsl:if>
</xsl:template>
above code will fillter data as two coloums. It is ok. Now I need to get the sum of <xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
I am from procedural programming. I read many articles but I still coudnt figure out that how to get the sum of this. can anybody help me?
Here is the xml
<TopHoldings Currency="xxx">
<Entry Type="CName||C||S||Fund Weight (%)||Benchmark weight (%)" Value="Ab||U||||1.2170000000000||" Date="8/31/2011" />
here is the whole xslt
<table style="width:50%;font-size:12px;" cellspacing="0" cellpadding="0">
<tr style="width:50%; text-align:left;background-color:E6F1F9;">
<th> </th>
<th> % of funds </th>
</tr>
<xsl:apply-templates select="$items">
<xsl:sort select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')" order="descending"/>
<xsl:sort select="substring-before(#Value,'||')"/>
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Entry">
<xsl:if test="position() <= 10">
<tr>
<td>
<xsl:value-of select="substring-before(#Value,'||')"/>
</td>
<td>
<xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
</td>
</tr>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When you need to sum the values of multiple values on XSLT 1.0 you have to rely on recursion [EDIT: in XSLT 1.0 the function sum it is also available] (in XSLT 2.0 there is an XPath function sum()).
The following template performs the sum of the given elements through the elements-to-sum parameter, extracting the value to sum from the attribute #Value as you specified.
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" />
<xsl:template match="TopHoldings">
<table>
<xsl:call-template name="sum">
<xsl:with-param name="elements-to-sum"
select="Entry" />
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="elements-to-sum" />
<xsl:param name="sum" select="'0'" />
<!-- Store in variables the following operations to avoid performing them more than once -->
<xsl:variable name="current-element" select="$elements-to-sum[1]" />
<xsl:variable name="current-value" select="format-number(substring(substring-after($current-element/#Value,'||||'),1,10),'#.#')" />
<!-- Output the table row -->
<tr>
<td><xsl:value-of select="substring-before($current-element/#Value, '||')" /></td>
<td><xsl:value-of select="$current-value" /></td>
</tr>
<!-- Determine whether continue -->
<xsl:choose>
<!-- Case END: we have just one element to sum so we perform the sum and we output the desired result -->
<xsl:when test="count($elements-to-sum) = 1">
<tr>
<td>Result:</td>
<td><xsl:value-of select="$current-value + $sum" /></td>
</tr>
</xsl:when>
<!-- Case RECURSION : we call this template again adding the current value to the sum and removing the first element from the parameter elements-to-sum -->
<xsl:otherwise>
<xsl:call-template name="sum">
<xsl:with-param name="elements-to-sum"
select="$elements-to-sum[position() > 1]" />
<xsl:with-param name="sum"
select="$sum + $current-value" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I assumed that you wanted to display the result of the sum as a new row in the same table, if that is not the case and you want to display the result elsewhere the solution would be slightly different. Tell me if this solution is acceptable to you or you need to display the sum outside the element.
NOTE: Instead of doing those 'complex' string operations in the attribute #Value, I would consider splitting all the information within that attribute into different attributes.
Just use the XPath function sum(), it is available in both XPath 2.0 for XSLT 2.0 and XPath 1.0 for XSLT 1.0 as described in http://www.w3.org/TR/xpath/#function-sum. If the numbers you want to get the sum of are attributes "Value" to an element "Entry" use sum(//Entry/#Value)" to grab all and sum up. Change this to get the 10 elements you want in your xml data.

XSLT 1.0 and string counting

So I'm trying to solve a problem in xslt which I would normally know how to do in an imperative language. I'm adding cells to a table from a list of xml elements, standard stuff. So:
<some-elements>
<element>"the"</element>
<element>"minds"</element>
<element>"of"</element>
<element>"Douglas"</element>
<element>"Hofstadter"</element>
<element>"and"</element>
<element>"Luciano"</element>
<element>"Berio"</element>
</some-elements>
However, I want to cut off one row and start a new one after a certain character maximum has been reached. So say I allow at the most, 20 characters per row. I'd end up with this:
<table>
<tr>
<td>"the"</td>
<td>"minds"</td>
<td>"of"</td>
<td>"Douglas"</td>
</tr>
<tr>
<td>"Hofstadter"</td>
<td>"and"</td>
<td>"Luciano"</td>
</tr>
<tr>
<td>"Berio"</td>
</tr>
</table>
In an imperative language, I'd append the elements to a row while adding each elements string-count to some mutable variable. When that variable exceeded 20, I'd stop, build a new row, and rerun the whole process (starting at the stopped element) on that row after returning the string-count to zero. However, I can't change variable values in XSLT. This whole stateless, function evaluation thing is throwing me for a loop.
Coming to this forum from xsl-list is like going back 10 years, why does everyone use xslt 1:-)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="some-elements">
<table>
<xsl:apply-templates select="element[1]"/>
</table>
</xsl:template>
<xsl:template match="element">
<xsl:param name="row"/>
<xsl:choose>
<xsl:when test="(string-length($row)+string-length(.))>20
or
not(following-sibling::element[1])">
<tr>
<xsl:copy-of select="$row"/>
<xsl:copy-of select="."/>
</tr>
<xsl:apply-templates select="following-sibling::element[1]"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="following-sibling::element[1]">
<xsl:with-param name="row">
<xsl:copy-of select="$row"/>
<xsl:copy-of select="."/>
</xsl:with-param>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>