loop string and get value xslt - xslt

I have html input file with that i need to process style information and design XSLT
<div style="width: 3%; min-width: 10px; padding: 0px; border-color: black; vertical-align: top; word-wrap: break-word; box-sizing: border-box;"></div>
i can get style value in XSLT variable, like below mention
<xsl:variable name="width-col">
<xsl:value-of select="#style"/>
</xsl:variable>
i don't know how to loop the string in foreach() and get width value out of it.
Thanks,

There is no need to "loop string". Use:
<xsl:value-of select="substring-before(substring-after(#style, 'width: '), ';')"/>

Related

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

XSLT 1.0 Greedy knapsack grouping methods?

I have an XML dataset (provided from SharePoint 2007 to a DVWP) structured something like:
<Rows>
<Row ID="1" Spanoffset="0" Span="55" Spantail="55"/>
<Row ID="2" Spanoffset="30" Span="31" Spantail="61"/>
<Row ID="3" Spanoffset="61" Span="20" Spantail="81"/>
<Row ID="4" Spanoffset="82" Span="30" Spantail="112"/>
</Rows>
Say each row represents a bar that starts at #Spanoffset and is #Span wide, #Spantail is there so I don't have to calculate it if I need it. I am trying to pack the rows together efficently so that rows that wont overlap get grouped together. The dataset is pre-sorted by #Spanoffset. This is essentially a knapsack problem as each Row could fit in multiple possible groups. What I want to do is a simple greedy solution, and know how I could code it in say c# or java, but since I cannot mark nodes as visited (well I can, but I lose it when I come back up the recursion tree) and I cannot seem to remove nodes as I visit them, I am at a loss for how to approach this.
For example the above data would look something like this:
<div style="clear:both">
<div style="width: 110px; margin-left: 0px; float:left;">1</div>
<div style="width: 40px; margin-left: 12px; float:left;">3</div>
<div style="width: 60px; margin-left: 2px; float:left;">4</div>
</div>
<div style="clear:both">
<div style="width: 62px; margin-left: 60px; float:left;">2</div>
</div>
I haven't been bothering trying to get the floats to work right as I have yet to be able to get the Row nodes to appear only one time each, in the right order. Once I get them there I am fairly certain I can get the formatting to work out.
The best XSLT I have come up with so far has been:
<xsl:template match="row">
<xsl:variable name="tail" select="#Spantail"/>
<div style="width:{2*#Span}px;
left:{2*(#Spanoffset)}px;">
<xsl:value-of select="#ID"/>
</div>
<xsl:apply-templates select="(following-sibling::row)[#Spanoffset>=$tail][1]"/>
</xsl:template>
Which generates
<div style="width: 110px;left: 0px">1</div>
<div style="width: 40px; left: 122px">3</div>
<div style="width: 60px; left: 164px">4</div>
<div style="width: 62px; left: 60px">2</div>
<div style="width: 40px; left: 122px">3</div>
<div style="width: 60px; left: 164px">4</div>
<div style="width: 40px; left: 122px">3</div>
<div style="width: 60px; left: 164px">4</div>
<div style="width: 60px; left: 164px">4</div>
So my problems are 2 (that I see) and I think they are intertwined.
1) How to fix/re-factor my template(s) to only emit each row once.
and
2) How to wrap the groupings in container <div> elements.
Been banging my head against this for 2 days, any one able to help?
Edit:
Well, after some sleep, I have the wrapping container by adding a boolean parameter to my template, and using some CDATA tags to emit <div> tags when its true. The boolean defaults to true, and when I call the nested apply-templates I set it to false, hence wrapping up the groups in containers. I still cant see a way of marking <Row>s as visited yet.
I think it's very tricky with just core XSLT, but it's easier with node-set(), a XSLT extension function:
<xsl:template name="add-row">
<xsl:param name="row"/>
<xsl:param name="prev-group" />
<xsl:if test="$row and not($row/#ID = $prev-group/Row/#ID)">
<xsl:copy-of select="$row" />
<xsl:call-template name="add-row">
<xsl:with-param name="row" select="$row/following-sibling::Row[#Spanoffset > $row/#Spantail][1]" />
<xsl:with-param name="prev-group" select="$prev-group" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="add-group">
<xsl:param name="first-row" />
<xsl:param name="prev-group" select="exsl:node-set(/)" />
<xsl:if test="$first-row">
<xsl:variable name="group">
<xsl:call-template name="add-row">
<xsl:with-param name="row" select="$first-row" />
<xsl:with-param name="prev-group" select="$prev-group" />
</xsl:call-template>
</xsl:variable>
<div clear="both">
<xsl:for-each select="exsl:node-set($group)/Row">
<div style="width: {2*#Span}px; left: {2*#Spanoffset}px"><xsl:value-of select="#ID"/></div>
</xsl:for-each>
</div>
<xsl:call-template name="add-group">
<xsl:with-param name="first-row" select="$first-row/following-sibling::Row[#Spanoffset < preceding-sibling::Row/#Spantail][1]" />
<xsl:with-param name="prev-group" select="exsl:node-set($group)" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="Rows">
<xsl:call-template name="add-group">
<xsl:with-param name="first-row" select="Row[1]" />
</xsl:call-template>
</xsl:template>
Don't forget to declare extension prefix and namespace in your stylesheet tag:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
extension-element-prefixes="exsl"
xmlns:exsl="http://exslt.org/common">
http://exslt.org/common is a valid namespace for Java XSLT processors, such as Xalan or Saxon; if you're using MSXML, use urn:schemas-microsoft-com:xslt instead.

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

Want to seperate table border width using xslt

Input html style::
border: #5f497a 3pt solid;
or
border: 3pt #5f497a solid;
or
border: solid #5f497a 3pt;
Hi all,
These all are my possible html input style from which i have to fetch only the border width (3) using xslt 1.0. Please help me..Thanks in advance..
Note:I will have always one digit before pt
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="table">
<xsl:apply-templates select="#style"/>
</xsl:template>
<xsl:template match="#style">
<xsl:if test="string-length() >0">
<xsl:variable name="vValues"
select="substring-after(.,':')"/>
<xsl:variable name="vNormalized" select=
"translate(normalize-space(concat(';',$vValues)),
' ',
';')
"/>
<xsl:variable name="vEndingWidth" select=
"substring-before($vNormalized,'pt;')"/>
<xsl:variable name="vLength"
select="string-length($vEndingWidth)"/>
<xsl:value-of select="substring($vEndingWidth, $vLength)"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document:
<t>
<table style="border: #5f497a 3pt solid;"/>
<table style="border: 3pt #5f497a solid;"/>
<table style="border: solid #5f497a 3pt;"/>
</t>
produces the wanted, correct results:
3
3
3

master stylesheet sharing in XSLT

i would like to create a master template in XSLT, which could be stored in a separate file. Every other Page stylesheets share it, with xsl:import.
master.xslt
<xsl:template match="Page">
<html>
<head>
</head>
<body>
<call-template name="Content"/>
</body>
</html>
</xsl:template>
<xsl:stylesheet>
page.xslt
<xsl:stylesheet>
<xsl:import href="master.xslt"/>
<xsl:template match="/">
<apply-templates match="Page"/>
</xsl:template>
<xsl:template name="Content">
... apply something page-specific
</xsl:template>
</xsl:stylesheet>
page.xml
<Page>
... something page-specific
</Page>
Can i improve this solution?
i cannot start from master stylesheet, because i will need xsl:import everything.
i dont want master.xslt contain references on each particular page.
Another decision (which is against the xslt spirit) maybe such:
master.xslt
<xsl:template name="masterHead">
<html>
<head>
</head>
<body>
</xsl:template>
<xsl:template name=masterEnd>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
page.xslt
<xsl:stylesheet>
<xsl:import href="master.xslt"/>
<xsl:template match="/">
<call-template name=masterHead>
... apply something page-specific
<call-template name=masterEnd/>
</xsl:template>
</xsl:stylesheet>
we don't need any general root <Page> element.
Using <xsl:import> is the right design decision. This is exactly the main use-case this XSLT directive was intended for.
One can go further even more -- lookup for the <xsl:apply-imports> directive, and in addition to how an imported stylesheet can apply templates about whose actions and meaning it absolutely doesn't know anything. The latter is called Higher-Order-Functions and is implemented in XSLT with the FXSL library (written entirely in XSLT).
That looks about right to me... very common to what I have used in the past (although I've often used <xsl:include/>, but either should work). The main change I might make is to make the match more explicit (at least in the master xslt) - i.e.
<xsl:template match="/Page"> <!-- leading slash -->
so it won't accidentally match Page elements at other locations (for example, data-paging, like <Page Index="3" Size="20"/>).
One other common thing I do is to add a "*" match that uses xsl:message to throw an error if I don't have a more-specific match for a node. This makes it more obvious when you have a typo, etc.
I'm actually glad to have found this example as I've been looking for verification that this is actually the correct approach to a master/slave template setup.
However the examples provided did not work out of the box on tomcat - so just to help others who only knows how to copy paste here are a working tomcat set of master / slave files.
Master.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" encoding="iso-8859-15" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" indent="no"/>
<!-- http://stackoverflow.com/questions/646878/master-stylesheet-sharing-in-xslt -->
<xsl:template match="ms247">
<html>
<head>
<title>test</title>
</head>
<body>
<div style="border: 1px solid black; width: 200px; float: left; margin: 10px; padding: 5px;">
<xsl:call-template name="left"/>
</div>
<div style="border: 1px solid black; width: 200px; float: left; margin: 10px; padding: 5px;">
<xsl:call-template name="content"/>
</div>
<div style="border: 1px solid black; width: 200px; float: left; margin: 10px; padding: 5px;">
<xsl:call-template name="right"/>
</div>
</body>
</html>
</xsl:template>
<xsl:template name="content">
<span style="color: red">Content template is empty - overrule in page template.</span>
</xsl:template>
<xsl:template name="left">
<span style="color: red">Left template is empty - overrule in page template.</span>
</xsl:template>
<xsl:template name="right">
<span style="color: red">Right template is empty - overrule in page template.</span>
</xsl:template>
</xsl:stylesheet>
And slave.xsl:
<?xml version="1.0" encoding="iso-8859-1" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="master.xsl"/>
<xsl:template name="content">
... apply something page-specific
</xsl:template>
<xsl:template name="right">
And we have RIGHT content!
<!-- Execute matching template which is NOT triggered automatically -->
<xsl:apply-templates select="params/param"/>
</xsl:template>
<!-- And we do not define any left template -->
<!-- Example -->
<xsl:template match="ms247/params/param">
Paramters on page: <xsl:value-of select="#name"/><br/>
</xsl:template>
</xsl:stylesheet>
Hope this can help others - do not be shy to drop me a note.