Extracting the attributes when the occurance of them is not particular - xslt

I am working on the extracting the values background color and width attributes from "td" of a table.
There are several ways of occurance of Back ground color and width. T
I have following set of valid elements.
1.<td style="BACKGROUND-COLOR: yellow; WIDTH: 52%"></td>
(combination of BACKGROUND-COLOR and Width in one order)
2.<td style="WIDTH: 52%;BACKGROUND-COLOR: green"></td>
(combination of BACKGROUND-COLOR and Width in another order)
3.<td style="WIDTH:52%;BACKGROUND-COLOR: green"></td>
(Spaces could vary from ":" and value)
4.<td style="BACKGROUND-COLOR: gray"></td>
(only BACKGROUND-COLOR in style)
5.<td style="BACKGROUND-COLOR: Gray"></td>
(Value of BACKGROUND-COLOR can be case sensitive)
6.<td style="BACKGROUND-COLOR: #ffff00"></td>
(value of BACKGROUND-COLOR can be hexadecimal also)
7.<td bgcolor="#008000" style="WIDTH: 54%">
(BACKGROUND-COLOR can occur as bgcolr also(hexadecimal code)
8.<td bgcolor="yellow">
(BACKGROUND-COLOR can occur as bgcolr also(string))
List of valid colors and their codes:(all the values are case sensitive)
Yellow:#ffff00
Gray:#808080
Green:#008000
Output:
1.<bgclr>Yellow</bgclr>
<colwidth>52</colwidth>
2.<bgclr>Green</bgclr>
<colwidth>52</colwidth>
3. <bgclr>Green</bgclr>
<colwidth>52</colwidth>
4.<bgclr>Gray</bgclr>
5.<bgclr>Gray</bgclr>
6.<bgclr>Yellow</bgclr>
7.<bgclr>Green</bgclr>
<colwidth>54</colwidth>
8.<bgclr>Yellow</bgclr>
I have tried my level best to solve this, and it looks complicated for me.
I am also providing the valid xml file.
<tr>
<td style="BACKGROUND-COLOR: yellow; WIDTH: 52%"></td>
<td style="WIDTH: 52%;BACKGROUND-COLOR: green"></td>
<td style="WIDTH:52%;BACKGROUND-COLOR: green"></td>
<td style="BACKGROUND-COLOR: gray"></td>
<td style="BACKGROUND-COLOR: Gray"></td>
<td style="BACKGROUND-COLOR: #ffff00"></td>
<td bgcolor="#008000" style="WIDTH: 54%"></td>
<td bgcolor="yellow"></td>
</tr>
Can any one help on this.
Thanks.

We can leverage the power of xsl:apply-templates and predicates to avoid a lot of painful xsl:choose statements. A template approach will also give us a more modular solution.
This XSLT 1.0 style-sheet ...
<?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" indent="yes"/>
<xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:template match="/">
<tr>
<xsl:apply-templates select="*/td"/>
</tr>
</xsl:template>
<xsl:template match="td">
<td>
<xsl:apply-templates
select="self::*[contains(#style,'BACKGROUND-COLOR:') or #bgcolor]"
mode="bg-colour"/>
<xsl:apply-templates
select="self::*[contains(#style,'WIDTH:')]"
mode="width"/>
</td>
</xsl:template>
<xsl:template match="td[#bgcolor]" mode="bg-colour">
<xsl:call-template name="render-bg-colour">
<xsl:with-param name="raw-colour" select="#bgcolor" />
</xsl:call-template>
</xsl:template>
<xsl:template match="td" mode="bg-colour">
<xsl:call-template name="render-bg-colour">
<xsl:with-param name="raw-colour" select="
substring-before( substring-after(concat(#style,';'),'BACKGROUND-COLOR:'), ';')" />
</xsl:call-template>
</xsl:template>
<xsl:variable name="palette">
<colours>
<colour>
<code>#ffff00</code>
<name>Yellow</name>
</colour>
<colour>
<code>#808080</code>
<name>Gray</name>
</colour>
<colour>
<code>#008000</code>
<name>Green</name>
</colour>
</colours>
</xsl:variable>
<xsl:template name="render-bg-colour">
<xsl:param name="raw-colour" />
<xsl:variable name="trim-colour" select="normalize-space( $raw-colour)" />
<xsl:variable name="canon-colour" select="
document('')//xsl:variable[#name='palette']/colours/colour[
(translate($trim-colour, $uppercase, $lowercase) =
translate(name , $uppercase, $lowercase) ) or
($raw-colour = code)
]/name/text()"/>
<bgclr>
<xsl:value-of select="$canon-colour" />
</bgclr>
</xsl:template>
<xsl:template match="td" mode="width">
<colwidth>
<xsl:value-of select="
normalize-space( substring-before( substring-after(#style,'WIDTH:'), '%'))" />
</colwidth>
</xsl:template>
</xsl:stylesheet>
... when applied to this input document ...
<tr>
<td style="BACKGROUND-COLOR: yellow; WIDTH: 52%"></td>
<td style="WIDTH: 52%;BACKGROUND-COLOR: green"></td>
<td style="WIDTH:52%;BACKGROUND-COLOR: green"></td>
<td style="BACKGROUND-COLOR: gray"></td>
<td style="BACKGROUND-COLOR: Gray"></td>
<td style="BACKGROUND-COLOR: #ffff00"></td>
<td bgcolor="#008000" style="WIDTH: 54%"></td>
<td bgcolor="yellow"></td>
</tr>
... will yield this output document...
<tr>
<td>
<bgclr>Yellow</bgclr>
<colwidth>52</colwidth>
</td>
<td>
<bgclr>Green</bgclr>
<colwidth>52</colwidth>
</td>
<td>
<bgclr>Green</bgclr>
<colwidth>52</colwidth>
</td>
<td>
<bgclr>Gray</bgclr>
</td>
<td>
<bgclr>Gray</bgclr>
</td>
<td>
<bgclr>Yellow</bgclr>
</td>
<td>
<bgclr>Green</bgclr>
<colwidth>54</colwidth>
</td>
<td>
<bgclr>Yellow</bgclr>
</td>
</tr>

Related

Using multiple regex in XSLT : how to reduce processing time

I am quite new to programming and XSLT: I try to improve the way I ask questions and explain problems, but I still have a long way to go. Sorry if there is something unclear.
I need to detect various alphabets in my XML document, which looks like this, with a lot more different language options.
<text>
<p>Some text. dise´mbər Some text. Some text.</p> <!-- text in International Phonetic Alphabet + English -->
<p>Some text. dise´mbər Some text. Издательство Академии Наук СССР Some text.</p> <!-- text in International Phonetic Alphabet + English + Cyrillic alphabet -->
<p>Some text. Издательство Академии Наук СССР dise´mbər Some text. Some text.</p>
<p>Some text. Some text. Издательство Академии Наук СССР Some text.</p> <!-- text in English + Cyrillic alphabet -->
</text>
What I started to do in XSLT is this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xml" indent="no" encoding="UTF-8" omit-xml-declaration="no" />
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:for-each select="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="processing-instruction()">
<xsl:processing-instruction name="{local-name()}"><xsl:apply-templates></xsl:apply-templates></xsl:processing-instruction>
</xsl:template>
<xsl:template name="IPA">
<xsl:variable name="text" ><xsl:copy-of select="."/></xsl:variable>
<xsl:analyze-string select="$text" regex="((\p{{IsIPAExtensions}}|\p{{IsPhoneticExtensions}})+)" >
<xsl:matching-substring>
<IPA><xsl:value-of select="regex-group(1)"/></IPA>
</xsl:matching-substring>
<xsl:non-matching-substring><xsl:copy-of select="."></xsl:copy-of></xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template name="Cyrillic">
<xsl:variable name="texte" ><xsl:call-template name="IPA"></xsl:call-template></xsl:variable>
<xsl:analyze-string select="$texte" regex="(\p{{IsCyrillic}}+)" >
<xsl:matching-substring>
<Cyrillic><xsl:apply-templates select="regex-group(1)"/></Cyrillic>
</xsl:matching-substring>
<xsl:non-matching-substring><xsl:call-template name="IPA"></xsl:call-template></xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="Cyrillic"></xsl:call-template>
</xsl:template>
</xsl:stylesheet>
So that I could get an XML like this:
<?xml version="1.0" encoding="UTF-8"?><text>
<p>Some text. dise´mb<IPA>ə</IPA>r Some text. Some text.</p>
<p>Some text. dise´mb<IPA>ə</IPA>r Some text. <Cyrillic>Издательство</Cyrillic> <Cyrillic>Академии</Cyrillic> <Cyrillic>Наук</Cyrillic> <Cyrillic>СССР</Cyrillic> Some text.</p>
<p>Some text. <Cyrillic>Издательство</Cyrillic> <Cyrillic>Академии</Cyrillic>
<Cyrillic>Наук</Cyrillic> <Cyrillic>СССР</Cyrillic> dise´mb<IPA>ə</IPA>r Some text. Some text.</p>
<p>Some text. Some text. <Cyrillic>Издательство</Cyrillic> <Cyrillic>Академии</Cyrillic>
<Cyrillic>Наук</Cyrillic> <Cyrillic>СССР</Cyrillic> Some text.</p>
</text>
This is what I needed, however, there is a ten or so regex blocks that I use and the processing time will be quite long if I use this method. What would you do instead? Do you think XSLT is appropriate for this?
Thank you !
Maria
(XSLT 2, Saxon-HE 9.8.0.8)
Edit: here's the profile:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Analysis of Stylesheet Execution Time</title>
</head>
<body>
<h1>Analysis of Stylesheet Execution Time</h1>
<p>Total time: 72128.065 milliseconds</p>
<h2>Time spent in each template, function or global variable:</h2>
<p>The table below is ordered by the total net time spent in the template, function
or global variable. Gross time means the time including called templates and functions
(recursive calls only count from the original entry); net time means time excluding
time spent in called templates and functions.
</p>
<table border="border" cellpadding="10">
<thead>
<tr>
<th>file</th>
<th>line</th>
<th>instruction</th>
<th>count</th>
<th>average time (gross/ms)</th>
<th>total time (gross/ms)</th>
<th>average time (net/ms)</th>
<th>total time (net/ms)</th>
</tr>
</thead>
<tbody>
<tr>
<td> "*code/unicode.xsl" </td>
<td>21</td>
<td>template Greek</td>
<td align="right">2,755,968</td>
<td align="right">0.017</td>
<td align="right">46,854.785</td>
<td align="right">0.017</td>
<td align="right">46,854.785</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>32</td>
<td>template Hebrew</td>
<td align="right">1,329,696</td>
<td align="right">0.043</td>
<td align="right">57,529.163</td>
<td align="right">0.008</td>
<td align="right">10,674.378</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>54</td>
<td>template IPA</td>
<td align="right">333,984</td>
<td align="right">0.206</td>
<td align="right">68,964.076</td>
<td align="right">0.019</td>
<td align="right">6,381.186</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>43</td>
<td>template Cyrillic</td>
<td align="right">665,392</td>
<td align="right">0.094</td>
<td align="right">62,582.890</td>
<td align="right">0.008</td>
<td align="right">5,053.727</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>65</td>
<td>template Arabic</td>
<td align="right">167,068</td>
<td align="right">0.421</td>
<td align="right">70,284.800</td>
<td align="right">0.008</td>
<td align="right">1,320.724</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>76</td>
<td>template Arrows</td>
<td align="right">83,536</td>
<td align="right">0.849</td>
<td align="right">70,945.946</td>
<td align="right">0.008</td>
<td align="right">661.146</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>8</td>
<td>template *</td>
<td align="right">12,122</td>
<td align="right">5.959</td>
<td align="right">72,238.100</td>
<td align="right">0.034</td>
<td align="right">413.937</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>87</td>
<td>template Dingbats</td>
<td align="right">41,768</td>
<td align="right">1.708</td>
<td align="right">71,323.074</td>
<td align="right">0.009</td>
<td align="right">377.128</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>98</td>
<td>template Private</td>
<td align="right">20,884</td>
<td align="right">3.427</td>
<td align="right">71,576.916</td>
<td align="right">0.012</td>
<td align="right">253.842</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>18</td>
<td>template processing-instruction()</td>
<td align="right">6,907</td>
<td align="right">0.014</td>
<td align="right">98.490</td>
<td align="right">0.014</td>
<td align="right">98.490</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>121</td>
<td>template text()</td>
<td align="right">20,884</td>
<td align="right">3.429</td>
<td align="right">71,600.976</td>
<td align="right">0.001</td>
<td align="right">24.060</td>
</tr>
</tbody>
</table>
</body>
</html>
The profile of Martin Honnen's code:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Analysis of Stylesheet Execution Time</title>
</head>
<body>
<h1>Analysis of Stylesheet Execution Time</h1>
<p>Total time: 2900.594 milliseconds</p>
<h2>Time spent in each template, function or global variable:</h2>
<p>The table below is ordered by the total net time spent in the template, function
or global variable. Gross time means the time including called templates and functions
(recursive calls only count from the original entry); net time means time excluding
time spent in called templates and functions.
</p>
<table border="border" cellpadding="10">
<thead>
<tr>
<th>file</th>
<th>line</th>
<th>instruction</th>
<th>count</th>
<th>average time (gross/ms)</th>
<th>total time (gross/ms)</th>
<th>average time (net/ms)</th>
<th>total time (net/ms)</th>
</tr>
</thead>
<tbody>
<tr>
<td> "*code/unicode.xsl" </td>
<td>44</td>
<td>template text()</td>
<td align="right">222,968</td>
<td align="right">0.009</td>
<td align="right">1,949.720</td>
<td align="right">0.009</td>
<td align="right">1,949.720</td>
</tr>
<tr>
<td> "*code/unicode.xsl" </td>
<td>26</td>
<td>template text()</td>
<td align="right">20,884</td>
<td align="right">0.135</td>
<td align="right">2,823.597</td>
<td align="right">0.042</td>
<td align="right">873.877</td>
</tr>
</tbody>
</table>
</body>
</html>
Regular expressions such as \p{IsIPAExtensions} should be reasonably efficient: most of the blocks are a single consecutive range of codepoints and testing a character should simply check whether it is in that range. The cost, I suspect, arises not so much from the cost of checking one character against one Unicode block, but from the number of characters and the number of blocks.
It might be worth getting a profile at the Java level to see where it is spending its time. I can guess, but a profile would reveal if my guess is right.
The thing that can kill performance with regular expressions is backtracking, but I don't immediately see any risk of backtracking with this code.
The only other approach that comes to mind is to generate an enormous translate() call that classifies characters into groups (so all latin characters become "1", all Cyrillic characters become "2", etc) and then to process the result using `<xsl:for-each-group select="string-to-codepoints(.)" group-adjacent=".">. But there's no guarantee that would perform any better, and it's a lot of work to do the experiments to find out.
In XSLT 3, I would consider the following approach:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 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"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="scripts"
as="map(xs:string, xs:string)*"
select="map { 'Cyrillic' : '\p{IsCyrillic}+'},
map { 'IPA' : '[\p{IsIPAExtensions}\p{IsPhoneticExtensions}]+' }"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="text()">
<xsl:iterate select="$scripts">
<xsl:param name="input" select="."/>
<xsl:on-completion>
<xsl:sequence select="$input"/>
</xsl:on-completion>
<xsl:next-iteration>
<xsl:with-param name="input">
<xsl:apply-templates select="$input" mode="wrap">
<xsl:with-param name="script-map" tunnel="yes" select="."/>
</xsl:apply-templates>
</xsl:with-param>
</xsl:next-iteration>
</xsl:iterate>
</xsl:template>
<xsl:mode name="wrap" on-no-match="shallow-copy"/>
<xsl:template match="text()" mode="wrap">
<xsl:param name="script-map" tunnel="yes"/>
<xsl:analyze-string select="." regex="{$script-map?*}">
<xsl:matching-substring>
<xsl:element name="{map:keys($script-map)}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
I haven't measured whether it performs better but for the regular expressions I would [\p{IsIPAExtensions}\p{IsPhoneticExtensions}]+ consider to be easier than (\p{IsIPAExtensions}|\p{IsPhoneticExtensions})+.
The other improvements are to rely on the xsl:mode based identity transformation and xsl:iterate.

How to use <Xsl: apply-templates match="othertemplate" > inside another template <xsl:template match="sometemplate">

Curent output Expected output I am trying to populate a dropdown list using a xsl template inside another xsl template match. I get empty dropdown. How to get the values in the dropdwon from a xsl template ?
I have 2 templates. Template 1 has a list of preferences and template 2 has list of user details. I am trying to populate a table with list of user details. And i need to populate the preference list as once of the column in table as drop down values. I am getting an empty dropdown list. attached image for reference
XML inputs
<USERLIST>
<record num="0"><SERIALNUMER>01</SERIALNUMER><NAME>Rahul</NAME>
<SELECTEDPREFERENCE>Pref2</SELECTEDPREFERENCE></record>
<record num="1"><SERIALNUMER>02</SERIALNUMER><NAME>Khan</NAME>
<SELECTEDPREFERENCE>Pref4</SELECTEDPREFERENCE></record>
<record num="2"><SERIALNUMER>03</SERIALNUMER><NAME>Raju</NAME>
<SELECTEDPREFERENCE>Pref2</SELECTEDPREFERENCE></record>
</USERLIST>
<PREFERENCE_LIST>
<record num="0"><PREFERENCE_ID>pref1</PREFERENCE_ID></record>
<record num="1"><PREFERENCE_ID>pref2</PREFERENCE_ID></record>
<record num="2"><PREFERENCE_ID>pref3</PREFERENCE_ID></record>
<record num="3"><PREFERENCE_ID>pref4</PREFERENCE_ID></record>
<record num="4"><PREFERENCE_ID>pref5</PREFERENCE_ID></record>
</PREFERENCE_LIST>
XSLT
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" />
<xsl:template match="/page">
<div style="margin: 0px; background-color: #ffffff;">
<form action="Useraction" method="post" id="user_search" onsubmit="if (document.getElementById('user_search').action.value=='') return false; else return true;" style="margin: 0px; border: 0px; padding-top: 0px;">
<hr style="color: #003399; width: 100%; height: 3px; border: 0px; margin-top: 0px; margin-bottom: 5px;" />
<table style="width: 100%; text-align: center; border: 0; background-color: #ffffff; margin-bottom: 1px; margin-top: 1px;" cellspacing="0" cellpadding="0">
<tr>
<td class="tableheader3" style="white-space: nowrap; width: 30px;height:30px">
<p align="center">
<input class="tabletext" type="button" value="Alle" onclick="javascript:markAll(document.getElementById('user_search'), 'UserId_')" style="width: 50px;" />
</p>
</td>
<td class="tableheader5" style="width: 30px; text-align: center;">Serial Number</td>
<td class="tableheader5" style="white-space: nowrap; width: 60px;">Name</td>
<td class="tableheader5" style="white-space: nowrap; width: 40px;">Preference</td>
</tr>
<tr>
<td colspan="12">
<hr style="border-top: 1px solid #1A15B7; background: transparent;"> </hr>
</td>
</tr>
<xsl:if test="USERLIST/record">
<xsl:apply-templates select="USERLIST/record" />
<tr>
<td colspan="12">
<img src="images/td_background4.jpg" style="width: 100%; height: 5px; border: 0px;" alt="" />
</td>
</tr>
</xsl:if>
</table>
</form>
</div>
</xsl:template>
<xsl:template match="PREFERENCE_LIST/record">
<xsl:if test="PREFERENCE_ID=/page/parameters/preference">
<option value="{PREFERENCE_ID }" selected="selected ">
<xsl:value-of select="PREFERENCE_ID " />
</option>
</xsl:if>
<xsl:if test="not(PREFERENCE_ID =/page/parameters/preference)">
<option value="{PREFERENCE_ID }">
<xsl:value-of select="PREFERENCE_ID " />
</option>
</xsl:if>
</xsl:template>
<xsl:template match="USERLIST/record">
<tr>
<td class="tabletext" style="white-space: nowrap;">
<xsl:value-of select="SERIALNUMER" />
</td>
<td class="tabletext" style="white-space: nowrap;">
<xsl:value-of select="NAME" />
</td>
<td class="tabletext" style="white-space: nowrap;">
<select class="tabletext" name="preference" style="width:79px;">
<option><xsl:attribute name="value"><xsl:value-of select="SELECTEDPREFERENCE"/>
</xsl:attribute></option>
<option value="" />
<xsl:apply-templates name="PREFERENCE_LIST/record" />
</select>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
As mentioned in comments, name is not a valid attribute on xsl:apply-templates, but assuming you changed it to select the reason it did not select anything is that you are currently in a template matching a record element, and so doing <xsl:apply-templates select="PREFERENCE_LIST/record" /> will look for a child element of record called PREFERENCE_LIST, which does not exist.
You need to navigate back up two levels in the hierarchy to get the PREFERENCE_LIST, so you should actually be writing this:
<xsl:apply-templates select="../../PREFERENCE_LIST/record" />
I am going to hazard a guess that you want the user's SELECTEDPREFERENCE selected as an option in the drop-down list? In this case, you need to pass the user's selected preference to the record template.
Try this two templates instead
<xsl:template match="PREFERENCE_LIST/record">
<xsl:param name="pref" />
<option value="{PREFERENCE_ID}">
<xsl:if test="$pref = PREFERENCE_ID">
<xsl:attribute name="selected">selected</xsl:attribute>
</xsl:if>
<xsl:value-of select="PREFERENCE_ID " />
</option>
</xsl:template>
<xsl:template match="USERLIST/record">
<tr>
<td class="tabletext" style="white-space: nowrap;">
<xsl:value-of select="SERIALNUMER" />
</td>
<td class="tabletext" style="white-space: nowrap;">
<xsl:value-of select="NAME" />
</td>
<td class="tabletext" style="white-space: nowrap;">
<select class="tabletext" name="preference" style="width:79px;">
<option value="" />
<xsl:variable name="selectPref" select="translate(SELECTEDPREFERENCE, 'P', 'p')" />
<xsl:apply-templates select="../../PREFERENCE_LIST/record">
<xsl:with-param name="pref" select="$selectPref" />
</xsl:apply-templates>
</select>
</td>
</tr>
</xsl:template>
Note the use of translate was simply because the check would be case-sensitive, and so Pref2 would not match pref2.
See it in action at http://xsltfiddle.liberty-development.net/ncdD7mS

Data Bars through stylesheet transform?

i have some data in XML format. For example:
<?xml version="1.0"?>
<TableSizes>
<Table name="AuditLog" rows="13193925" reserved="4896864" data="2522592" indexSize="2373824" unused="448"/>
<Table name="Customers" rows="7021839" reserved="3243392" data="1480640" indexSize="1762640" unused="112"/>
</TableSizes>
i would like that data to be transformed and displayed by the client. So when i feed the User Agent the xml, i supply it a stylesheet:
<?xml version="1.0"?>
<?xml-stylesheet type='text/xsl' href='databaseSize.xslt' media='all'?>
<TableSizes>
<Table name="AuditLog" rows="13193925" reserved="4896864" data="2522592" indexSize="2373824" unused="448"/>
<Table name="Customers" rows="7021839" reserved="3243392" data="1480640" indexSize="1762640" unused="112"/>
</TableSizes>
This causes it to be transformed into some suitable HTML. And that works well enough.
But now i want to add Data Bars:
As it is now, i've been generating the HTML on the server (i.e. the server is deciding how to display content, rather than a stylesheet):
Which requires each table cell to have a custom background style applied, with the computed gradient stops:
<TR>
<TD>AuditLog
<TD style="background: linear-gradient(to right, #658FC6 0%,rgb(255,255,255) 100%,rgb(255,255,255) 100%)">1319,,3925
<TD style="background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 100%,rgb(255,255,255) 100%)">489,,6864
<TD>252,,2592
<TD>237,,3824
<TD>448
</TR>
<TR>
<TD>Customers
<TD style="background: linear-gradient(to right, #658FC6 0%,rgb(255,255,255) 51%,rgb(255,255,255) 100%)">702,,1839
<TD style="background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 62%,rgb(255,255,255) 100%)">324,,3392
<TD>148,,0640
<TD>176,,2640
<TD>112
</TR>
Obviously i'd rather have all of this performed in the client by a stylesheet, rather than doing it on the server.
Possible?
Note: If it's not: it's not a problem; just say so.
What XSLT do i have so far?
Essentially, none. The XST i have so far does nothing to add data bars:
<TD><xsl:value-of select="#name"/>
<TD><xsl:value-of select="#rows"/>
<TD><xsl:value-of select="#reserved"/>
<TD><xsl:value-of select="#data"/>
<TD><xsl:value-of select="#indexSize"/>
<TD><xsl:value-of select="#unused"/>
i have add a dummy amount of gradient drawing code:
<TD><xsl:value-of select="#name"/>
<TD style='background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 50%,rgb(255,255,255) 100%)'><xsl:value-of select="#rows"/>
<TD style='background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 50%,rgb(255,255,255) 100%)'><xsl:value-of select="#reserved"/>
<TD><xsl:value-of select="#data"/>
<TD><xsl:value-of select="#indexSize"/>
<TD><xsl:value-of select="#unused"/>
Strictly speaking, i don't even have that XSLT. i didn't want to write an entire XSLT when it cannot accomplish what i want. The XSLT i have above i created on-the-fly when someone suggested that the question isn't work answering until i at least pretend to have some XSLT first.
You can do the calculations pretty easily in XSLT and put that into a style attribute.
I created a template for each AuditLog and Customers. And then in the Customers template I just defined a variable for the previous values in AuditLog to use in the calculation. From there I used each to figure out the percent needed for the gradient(along with some quick 'rounding').
<xsl:value-of select="substring(((#rows div $PrevRow) * 100),1,2)"/>
So when you take your XML and transform it with this XSL
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="Table[#name='AuditLog']">
<tr>
<td>
<xsl:value-of select="#name"/>
</td>
<td style="background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 100%,rgb(255,255,255) 100%)">
<xsl:value-of select="#rows"/>
</td>
<td style="background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 100%,rgb(255,255,255) 100%)">
<xsl:value-of select="#reserved"/>
</td>
<td>
<xsl:value-of select="#data"/>
</td>
<td>
<xsl:value-of select="#indexSize"/>
</td>
<td>
<xsl:value-of select="#unused"/>
</td>
</tr>
</xsl:template>
<xsl:template match="Table[#name='Customers']">
<xsl:variable name="PrevRow" select="preceding-sibling::Table/#rows"/>
<xsl:variable name="PrevReserved" select="preceding-sibling::Table/#reserved"/>
<tr>
<td>
<xsl:value-of select="#name"/>
</td>
<td>
<xsl:attribute name="style">
<xsl:text>background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) </xsl:text>
<xsl:value-of select="substring(((#rows div $PrevRow) * 100),1,2)"/>
<xsl:text>%,rgb(255,255,255) 100%)</xsl:text>
</xsl:attribute>
<xsl:value-of select="#rows"/>
</td>
<td>
<xsl:attribute name="style">
<xsl:text>background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) </xsl:text>
<xsl:value-of select="substring(((#reserved div $PrevReserved) * 100),1,2)"/>
<xsl:text>%,rgb(255,255,255) 100%)</xsl:text>
</xsl:attribute>
<xsl:value-of select="#reserved"/>
<br/>
<xsl:value-of select="$PrevReserved"/>
</td>
<td>
<xsl:value-of select="#data"/>
</td>
<td>
<xsl:value-of select="#indexSize"/>
</td>
<td>
<xsl:value-of select="#unused"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
You get this output:
<tr>
<td>AuditLog</td>
<td style="background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 100%,rgb(255,255,255) 100%)">13193925</td>
<td style="background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 100%,rgb(255,255,255) 100%)">4896864</td>
<td>2522592</td>
<td>2373824</td>
<td>448</td>
</tr>
<tr>
<td>Customers</td>
<td style="background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 53%,rgb(255,255,255) 100%)">7021839</td>
<td style="background: linear-gradient(to right, #FF595E 0%,rgb(255,255,255) 66%,rgb(255,255,255) 100%)">3243392<br/>4896864</td>
<td>1480640</td>
<td>1762640</td>
<td>112</td>
</tr>

Distinct Values in XSL1.0

I need to convert the following into XSL 1.0 compatibility
<xsl:value-of select="count(distinct-values(/ChangeLog/ChangeSets//WorkItems//WorkItem/_ID))"/>
Have tried
<xsl:value-of select="count(/ChangeLog/ChangeSets//WorkItems//WorkItem/_ID[not(.=following::_ID)])"/>
Here is the sample XML
<ChangeLog>
<ChangeSets>
<ChangeSet>
<ID>31</ID>
<Date>10/30/2012 2:05:59 AM</Date>
<Comment />
<User>XXX</User>
<WorkItems>
<WorkItem>
<_ID>2</_ID>
<_AreaID>1</_AreaID>
<_AuthorizedAs>XXX</_AuthorizedAs>
</WorkItem>
</WorkItems>
</ChangeSet>
<ChangeSet>
<ID>12</ID>
<Date>9/18/2012 7:30:43 AM</Date>
<Comment />
<User>XXX</User>
<WorkItems>
<WorkItem>
<_ID>1</_ID>
<_AreaID>1</_AreaID>
<_AuthorizedAs>XXX</_AuthorizedAs>
</WorkItem>
<WorkItem>
<_ID>2</_ID>
<_AreaID>1</_AreaID>
<_AuthorizedAs>XXX</_AuthorizedAs>
</WorkItem>
</WorkItems>
</ChangeSet>
<ChangeSet>
<ChangeSets>
</ChangeLog>
Here is the XSLT for the same .Mind this is in XSL 2.0 . I need it converted to XSL 1.0 and hence the question . I have updated the stylesheet version alone to point to XSL 1.0
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/ChangeLog">
<html>
<head>
<title>Release Notes Generated from TFS</title>
<style type="text/css">
.HeaderColumnStyle
{
width: 566px;
border: None;
}
.DataColumnStyle
{
border: none; width:auto;
white-space: -moz-pre-wrap; /* Firefox */
white-space: -pre-wrap; /* ancient Opera */
white-space: -o-pre-wrap; /* newer Opera */
white-space: pre-wrap; /* Chrome; W3C standard */
word-wrap: break-word; /* IE */
}
.DescriptionDataColumnStyle
{
border: none;
width: 850px;
white-space: -moz-pre-wrap; /* Firefox */
white-space: -pre-wrap; /* ancient Opera */
white-space: -o-pre-wrap; /* newer Opera */
white-space: pre-wrap; /* Chrome; W3C standard */
word-wrap: break-word; /* IE */
}
.style1
{
border-style: none;
border-color: inherit;
border-width: medium;
width: 60px;
}
</style>
</head>
<body>
<h1 align="center"> TFS Change Log Report</h1>
<table frame="box" border="3" style="width: 100%; border-bottom:#000080 outset;border-left:#000080 outset;border-top:#000080 outset;border-right:#000080 outset; text-align: left; vertical-align: middle;">
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>TFS Server : </xsl:text>
</b>
<xsl:apply-templates select="TFSServer/node()"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Project Name : </xsl:text>
</b>
<xsl:apply-templates select="TFSProjectName/node()"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Branch Location : </xsl:text>
</b>
<xsl:apply-templates select="TFSProjectBranchName/node()"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Report Produced By : </xsl:text>
</b>
<xsl:apply-templates select="ReportProducedBy/node()"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>From Changeset : </xsl:text>
</b>
<xsl:apply-templates select="FromChangeSet/node()"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>To Changeset : </xsl:text>
</b>
<xsl:apply-templates select="ToChangeSet/node()"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Total Number of Changesets : </xsl:text>
</b>
<xsl:value-of select="count(distinct-values(/ChangeLog//ChangeSet/ID))"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Total Number of Work Items : </xsl:text>
</b>
<xsl:value-of select="count(distinct-values(/ChangeLog/ChangeSets//WorkItems//WorkItem/_ID))"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Changesets with associated Work Items : </xsl:text>
</b>
<xsl:value-of select="count(/ChangeLog//ChangeSet[WorkItems/WorkItem/_ID[1] > 0])"/>
</td>
<td class="HeaderColumnStyle">
<b>
<xsl:text>Changesets without associated Work Items : </xsl:text>
</b>
<xsl:value-of select="count(distinct-values(/ChangeLog//ChangeSet/ID)) - count(/ChangeLog//ChangeSet[WorkItems/WorkItem/_ID[1] > 0])"/>
</td>
</tr>
<tr>
<td class="HeaderColumnStyle" colspan="2">
<b>
<xsl:text>Report Date : </xsl:text>
</b>
<xsl:apply-templates select="ReportDate/node()"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I have used this several times to select distinct values when doing search engine related products:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:key name="k1" match="_ID" use="."/>
<xsl:template match="/">
<xsl:for-each select="/path/to/iterate[generate-id() = generate-id(key('k1', .)[1])]">
<xsl:value-of select="."/>
<xsl:value-of select="''"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I think it will work for what you are trying to do.
Dave Pawson's XSLT FAQs are an invaluable source. Thanks Mr. Pawson! Here's some code ideas for "duplicates." http://www.dpawson.co.uk/xsl/sect2/N2696.html
The whole collection starts at http://www.dpawson.co.uk/xsl/sect2/sect21.html

XSL duplicate info problem

Just trying out xsl and I've got one problem that just won't go away. My style sheet code is this
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="Fighter">
<br/>
<br/>
<br/>
<b>Name </b> <xsl:value-of select="name"/> <br/>
<b>AKA</b> <xsl:value-of select="nickname"/> <br/>
<b>Age</b> <xsl:value-of select="age"/> <br/>
<b>Height</b> <xsl:value-of select="height"/> <br/>
<b>Division</b> <xsl:value-of select="division"/> <br/>
<b>Reach</b> <xsl:value-of select="reach"/> <br/>
<b>Stance</b> <xsl:value-of select="stance"/> <br/>
<b>Nationality</b> <xsl:value-of select="nationality"/> <br/>
<b>Training Camp</b> <xsl:value-of select="camp"/> <br/>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="bout">
<table width="100%" border="1">
<tr>
<td width ="10%"><xsl:value-of select="result"/></td>
<td width ="10%"><xsl:value-of select="opponent"/></td>
<td width ="10%"><xsl:value-of select="waywon"/></td>
<td width ="10%"><xsl:value-of select="round"/></td>
<td width ="10%"><xsl:value-of select="event"/></td>
<td width ="10%"><xsl:value-of select="date"/></td>
<td width ="10%"><xsl:value-of select="location"/></td>
<td width ="10%"><xsl:value-of select="notes"/></td>
</tr>
</table>
</xsl:template>
<xsl:template match="/">
<h1>LIST OF UFC FIGHTERS</h1>
<xsl:apply-templates/>
</xsl:template>
</xsl:transform>
I want to get some info on a fighter displed and then show the fight record as below. however it keeps on adding an extra line, as below.
Name George St Pierre AKA GSP Age 30 Height 5ft 10
in Division Welterweight Reach 76 in Stance
Orthodox Nationality Canadian Training Camp Tristar Gym
George St Pierre GSP 30 5ft 10 in Welterweight 76 in Orthodox Canadian
Tristar Gym
Why is this info printing out twice? I'm sure this is probably simple but it's really frustrating me.
Because you have apply-templates in template Fighter and no template for name, nickname, age etc - so XSL just copies text contented in this nodes - put this in the end of your XSL to avoid this and keep apply-templates there
<xsl:template match="*"></xsl:template>
This will put empty string for any tag not processed by other template
try this
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="Fighter">
<br/>
<br/>
<br/>
<b>Name </b> <xsl:value-of select="name"/> <br/>
<b>AKA</b> <xsl:value-of select="nickname"/> <br/>
<b>Age</b> <xsl:value-of select="age"/> <br/>
<b>Height</b> <xsl:value-of select="height"/> <br/>
<b>Division</b> <xsl:value-of select="division"/> <br/>
<b>Reach</b> <xsl:value-of select="reach"/> <br/>
<b>Stance</b> <xsl:value-of select="stance"/> <br/>
<b>Nationality</b> <xsl:value-of select="nationality"/> <br/>
<b>Training Camp</b> <xsl:value-of select="camp"/> <br/>
</xsl:template>
<xsl:template match="bout">
<table width="100%" border="1">
<tr>
<td width ="10%"><xsl:value-of select="result"/></td>
<td width ="10%"><xsl:value-of select="opponent"/></td>
<td width ="10%"><xsl:value-of select="waywon"/></td>
<td width ="10%"><xsl:value-of select="round"/></td>
<td width ="10%"><xsl:value-of select="event"/></td>
<td width ="10%"><xsl:value-of select="date"/></td>
<td width ="10%"><xsl:value-of select="location"/></td>
<td width ="10%"><xsl:value-of select="notes"/></td>
</tr>
</table>
</xsl:template>
<xsl:template match="/">
<h1>LIST OF UFC FIGHTERS</h1>
<xsl:apply-templates select="Fighter"/>
<xsl:apply-templates select="bout"/>
</xsl:template>
</xsl:transform>