Render HTML element only if a regex matches in XSLT - xslt

I want to parse an XML Document and render a table based on a certain string. If there is a specific code I don't want that table to be rendered. Here is the simplified code I'm currently using without regex:
<xsl:template match="assortment">
<tr>
<td>
<table class="tgtable tgAssortment">
<xsl:call-template name="countryCodes">
<xsl:with-param name="parStr" select="./countryCodes"/>
<xsl:with-param name="parPos" select="1"/>
</xsl:call-template>
</table>
</td>
<td><div class="tgData"><xsl:value-of select="./totalPackRatio"/></div></td>
<td><div class="tgData"><xsl:value-of select="./name"/></div></td>
</tr>
<xsl:apply-templates select="./styles/style"/>
</xsl:template>
The element ./countryCodes is a string like this:
CB1-AT,CB1-CH,CB1-DE,CB2-FR,CB3-EE,CB3-LT,CB3-LV,CB3-SE,CB4-CZ
Or like this:
CB8-OSDE,CB8-OSBE,CB8-OSNL,CB8-OSCZ,CB8-OSGB,CB8-OSFR,CB8-OSPL,CB8-OSSK
In the second example all country codes start wit CB8 in that case I don't want to render the complete <tr>. I wanted to check it with a simple regex like this: (CB(?!8)\d) and tried to implement it wit xsl:analyze. Here is my code:
<xsl:template match="assortment">
<xsl:analyze-string select="./countryCodes" regex="CB(?!8)\d">
<xsl:matching-substring>
<tr>
<td>
<table class="tgtable tgAssortment">
<xsl:call-template name="countryCodes">
<xsl:with-param name="parStr" select="./countryCodes"/>
<xsl:with-param name="parPos" select="1"/>
</xsl:call-template>
</table>
</td>
<td><div class="tgData"><xsl:value-of select="./totalPackRatio"/></div></td>
<td><div class="tgData"><xsl:value-of select="./name"/></div></td>
</tr>
<xsl:apply-templates select="./styles/style"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
This always gives me a stylesheet compilation error. I tried different regex and also using xsl:value-of as I thought it might be mandatory but this didn't work.
For debugging purposes I tried the following:
<xsl:template match="assortment">
<xsl:analyze-string select="./countryCodes" regex="CB(?!8)\d">
<xsl:matching-substring>
<div><xsl:value-of select="regex-group(1)"/></div>
</xsl:matching-substring>
</xsl:analyze-string>
<tr>
<td>
<table class="tgtable tgAssortment">
<xsl:call-template name="countryCodes">
<xsl:with-param name="parStr" select="./countryCodes"/>
<xsl:with-param name="parPos" select="1"/>
</xsl:call-template>
</table>
</td>
<td><div class="tgData"><xsl:value-of select="./totalPackRatio"/></div></td>
<td><div class="tgData"><xsl:value-of select="./name"/></div></td>
</tr>
<xsl:apply-templates select="./styles/style"/>
</xsl:template>
this was compiled successfully but obviously doesn't give the results I want. But I think it shows there is some problem with rendering the table inside the `xsl:analyze-string'. I'm stuck with this. Would appreciate any help. Thanks in advance!

If you only want to match on assortment elements where the countryCodes element has a comma delimited list of at least one code not starting with CB8 then using
<xsl:template match="assortment[not(every $token in tokenize(countryCodes, ',') satisfies starts-with($token, 'CB8'))]">
would do that.
As for a negative lookahead in regular expressions, that is not supported in the regular expression language XSLT and XPath 2.0 and later support, unless you use a processor dependent way to switch to the underlying platform's regular expression support e.g. for Saxon .NET use the flag ;n to swith to .NET's language, there you could have e.g.
<xsl:template match="assortment[every $token in tokenize(countryCodes, ',') satisfies matches($token, '^CB(?!8)\d', ';n')]">
I think but I am not quite sure which Saxon versions and editions support that.

Related

XSL conditions: how to choose a value from list only when the condition is met?

I am trying to implement a logic which would do choosing a value if it matches the condition. The problem is, if my list contains more elements than one, it wouldn't display the value which matches the condition, but only the first one. Could you provide some advice? thanks. I need this to implement multiple filtering by conditions. Meaning that in this for-each I would display only books with year 2008, but in another table I could also use 2010 from the list of years. The below example is a part of the template for one table which uses 2008, but I will have multiple tables where I would filter books by 2010 as well.
XSL:
<xsl:for-each select="years">
<xsl:choose>
<xsl:when test="year=2008">
<td class="year"><xsl:value-of select="year"/></td>
</xsl:when>
</xsl:choose>
</xsl:for-each>
XML:
<book>
<title>Professional ASP.NET 4 in C# and VB</title>
<author>Bill Evjen, Scott Hanselman, Devin Rader</author>
<country>USA</country>
<price>580</price>
<years>
<year>2010</year>
<year>2008</year>
</years>
</book>
This one would populate 2010 into my HTML, not 2008 as I would expect. Is this doable at all?
Update:
I've tried this approach for example to filter against multiple conditions separately:
<xsl:for-each select="library/book">
<tr>
<td class="filterTd title"><xsl:value-of select="title"/></td>
<td class="filterTd author"><xsl:value-of select="author"/></td>
<td class="filterTd price"><xsl:value-of select="price"/></td>
<xsl:for-each select="years/year[.2008]">
<td class="year">
<xsl:value-of select="."/>
</td>
</xsl:for-each>
<xsl:for-each select="years/year[.2010]">
<td class="year">
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
This would not populate any td at all in my case..
If you only want to display books for a specific year, you would put a condition in the xsl:for-each that selects the book
<xsl:for-each select="library/book[years/year=$year]">
Where $year is a variable (or parameter) containing the year you want
Do note, if you know you are dealing with a specific year, then you don't actually need to do <xsl:value-of select="year" />, you can just output the year value you are working with.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<xsl:call-template name="table">
<xsl:with-param name="year" select="2008" />
</xsl:call-template>
</xsl:template>
<xsl:template name="table">
<xsl:param name="year" />
<table>
<xsl:for-each select="library/book[years/year=$year]">
<tr>
<td class="filterTd title"><xsl:value-of select="title"/></td>
<td class="filterTd author"><xsl:value-of select="author"/></td>
<td class="filterTd price"><xsl:value-of select="price"/></td>
<td class="year">
<xsl:value-of select="$year"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Note, you could also use a key here to look up books by year
<xsl:key name="booksByYear" match="book" use="years/year" />
Then, the xsl:for-each to select books, looks like this:
<xsl:for-each select="key('booksByYear', $year)">

How to implement attribute specific templates without redundancy

I'm familiar with using templates that key off attributes as follows (e.g. key off of the presence of foo):
<xsl:template match="something[#foo]">
<xsl:template match="something[not(#foo)]">
However, if much of the content of these templates is the same, is there a better way that still uses templates, since the community appears to prefer them? Or is the solution to just use xsl:choose. It's clearly preferable not to write duplicate code that must be maintained in both templates.
EDIT:
Here's my specific set of templates:
<xsl:template match="item[not(#format)]">
<td class="{current()/../#name}_col status_all_col">
<xsl:value-of select="current()"/>
<xsl:value-of select="#units"/>
</td>
</xsl:template>
<xsl:template match="item[#format]">
<td class="{current()/../#name}_col status_all_col">
<xsl:value-of select="format-number(current(), #format)"/>
<xsl:value-of select="#units"/>
</td>
</xsl:template>
and here's what I currently have using choose:
<xsl:template match="item">
<td class="{current()/../#name}_col status_all_col">
<xsl:choose>
<xsl:when test="#format">
<xsl:value-of select="format-number(current(), #format)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="current()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="#units"/>
</td>
</xsl:template>
I would definitely use xsl:choose in this situation.
Just to demonstrate how you could use templates without code repetition:
<xsl:template match="item">
<td class="{../#name}_col status_all_col">
<xsl:apply-templates select="." mode="format"/>
<xsl:value-of select="#units"/>
</td>
</xsl:template>
<xsl:template match="item[not(#format)]" mode="format">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="item[#format]" mode="format">
<xsl:value-of select="format-number(., #format)"/>
</xsl:template>
But I fail to see what advantage this would have. On the contrary, it suffers from the GOTO syndrome.
BTW, this may not be the best example, since I believe that supplying an empty string as the second argument of the format-number() function will result in the number being returned as-is.
So you could just use a single template to match all:
<xsl:template match="item">
<td class="{../#name}_col status_all_col">
<xsl:value-of select="format-number(current(), #format)"/>
<xsl:value-of select="#units"/>
</td>
</xsl:template>

XSL stylesheet: creating Hyperlink based of query item_id

I am new to coding. Started XSL coding from past 1 month.
I want to creat a hyperlink according to the item_id.
But my concat is not working as desired.
My requirement is that i have to get create hyperlinks based on the variable item_id
For example:
https://xyz.com/webpr/webpr.php?objtype=frames&g_startlink=maintain&g_startdata=194970&g_userid=msbzzh&g_session_id=6017650`
https://xyz.com/webpr/webpr.php?objtype=frames&g_startlink=maintain&g_startdata=194971&g_userid=msbzzh&g_session_id=6017650
where the variable item_id comes inbetween the link. (194970, 194971 and so on)
So here is my code:
<xsl:when test ="$propName ='item_id'">
<td>
<xsl:variable name="itemId" select="$occRef/#*[local-name()=$propName]" />
<xsl:value-of select="$itemId" />
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
</td>
</xsl:when>
and i also tried like this.. But both of them didn't work out.
<xsl:value-of select="$itemId" />
UPDATED: You forgot to escape ampersands and indeed the variable was used incorrectly. See below the correct syntax.
<xsl:when test="$propName='item_id'">
<td>
<xsl:variable name="itemId" select="$occRef/#*[local-name()=$propName]"/>
<a href="{concat('https://xyz.com/webpr/webpr.php?objtype=frames&g_startlink=maintain&g_startdata=', $itemId, '&g_userid=msbzzh&g_session_id=6017650')}" target="_blank">
<xsl:value-of select="$itemId"/>
</a>
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
</td>
</xsl:when>

Arithmetic operation does not work when used in with-param in xsl

Could someone please explain why the following does not work.
Here is the xml:
<?xml version="1.0" ?>
<testsuites>
<testsuite errors="0" failures="0" name="test_ui_orchestration" tests="10" time="90.190"/>
<testsuite errors="1" failures="0" name="test_ui_tables" tests="13" time="1115.771"/>
<testsuite errors="0" failures="3" name="test_ui_dashboard" tests="18" time="116.397"/>
</testsuites>
I want to calculate total number of tests, total pass and fail. I have a problem getting total number of failures (failures + errors) and total number of pass (for simplicity: tests - failures). I call the same xml-template for calculating totals passing in different values using "with-param" as follows (section within "Calculate sums" comments):
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/testsuites">
<html>
<body>
<!-- Calculate sums start -->
<xsl:variable name="allSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#tests"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="failedSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#failures"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="errorSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#errors"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="failederrorSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#failures + testsuite/#errors"/>
</xsl:call-template>
</xsl:variable>
<!-- Calculate sums ends -->
<h2>Test Results</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Test Area</th>
<th>Total</th>
<th>Pass</th>
<th>Fail</th>
<th>Error</th>
<th>Fail and Error</th>
</tr>
<xsl:for-each select="testsuite">
<tr>
<td><xsl:value-of select="#name"/></td>
<td><xsl:value-of select="#tests"/></td>
<td><xsl:value-of select="#tests - (#failures + #errors)"/></td>
<td><xsl:value-of select="#failures"/></td>
<td><xsl:value-of select="#errors"/></td>
<td><xsl:value-of select="#failures + #errors"/></td>
<td> </td>
</tr>
</xsl:for-each>
<tr>
<td><b>All Tests</b></td>
<td><xsl:value-of select="$allSum"/></td>
<td>foo</td>
<!--
<td><xsl:value-of select="$passedSum"/></td>
-->
<td><xsl:value-of select="$failedSum"/></td>
<td><xsl:value-of select="$errorSum"/></td>
<td><xsl:value-of select="$failederrorSum"/></td>
<td></td>
</tr>
</table>
</body>
</html>
</xsl:template>
<xsl:template name="testsSum">
<xsl:param name="numTests"/>
<xsl:choose>
<xsl:when test="$numTests">
<xsl:variable name="recursive_result">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="$numTests[position() > 1]"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="number($numTests[1]) + $recursive_result"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This produces the following output for the sums (full output is at the end of this post):
41 3 1 0
while it should be:
41 3 1 4
Each individual sum works fine, the corresponding attribute is found. However, when I attempt to add two attributes and pass that to the sum template, it does not work as expected.
It gets worse. The code for generating total passed tests is:
<xsl:variable name="passedSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests"
select="testsuite/#tests - testsuite/#failures"/>
</xsl:call-template>
</xsl:variable>
When the above code is enabled, I get the following error:
Failed to evaluate the expression of variable 'numTests'
I've been searching online and understand that the "select" of "with-param" accepts "An XPath expression that defines the value of the parameter" (from w3schools). An XPath expression can contain arithmetic operations (http://www.w3schools.com/xpath/xpath_operators.asp), so why do the above fail?
Here is full output of the xsl transformation:
Test Area Total Pass Fail Error Fail and Error
----------------------------------------------------------------------
test_ui_orchestration 10 10 0 0 0
test_ui_tables 13 12 0 1 1
test_ui_dashboard 18 15 3 0 3
----------------------------------------------------------------------
All Tests 41 foo 3 1 0
Here is the expected output of the xsl transformation (modified values marked with *):
Test Area Total Pass Fail Error Fail and Error
----------------------------------------------------------------------
test_ui_orchestration 10 10 0 0 0
test_ui_tables 13 12 0 1 1
test_ui_dashboard 18 15 3 0 3
----------------------------------------------------------------------
All Tests 41 37* 3 1 4*
As Stormtroopr says, you haven't shown us the part of the XSLT code that sets the context, and the fact that you left this out suggests you haven't understood how important context is in XSLT. If your XPath expressions select anything at all, we can probably assume that the context item is the testsuites element, in which case path expressions such as testsuite/#failures all select multiple values (a node set containing several attribute nodes). That's fine if the template you are calling expects a node-set. But when you use a node-set containing multiple nodes as input to an arithmetic operation, then:
(a) In XSLT 1.0 it uses the first node in the node-set and ignores the others
(b) In XSLT 2.0 you get a type error saying that what you are doing makes no sense.
Since you're getting an error, I assume you are using XSLT 2.0, though it doesn't seem a very helpful error message (which XSLT processor are you using?)
As for fixing the problem, I can't help with that because you haven't explained what you are trying to achieve - I can't reverse-engineer the requirements from incorrect code.
Since the question has changed dramatically, I've made a new answer.
Your summing template (in fact most of the template) works fine. All you need to do to get those totals down the bottom, you just need to perform addition/subtraction on the variables you've already created:
<xsl:variable name="failederrorSum" select="$failedSum + $errorSum" />
<xsl:variable name="passedSum" select="$allSum - $failederrorSum" />
This stylesheet applied to the input XML:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/testsuites">
<html>
<body>
<!-- Calculate sums start -->
<xsl:variable name="allSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#tests"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="failedSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#failures"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="errorSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#errors"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="failederrorSum" select="$failedSum + $errorSum" />
<xsl:variable name="passedSum" select="$allSum - $failederrorSum" />
<!-- Calculate sums ends -->
<h2>Test Results</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Test Area</th>
<th>Total</th>
<th>Pass</th>
<th>Fail</th>
<th>Error</th>
<th>Fail and Error</th>
</tr>
<xsl:apply-templates />
<tr>
<td><b>All Tests</b></td>
<td><xsl:value-of select="$allSum"/></td>
<td><xsl:value-of select="$passedSum"/></td>
<td><xsl:value-of select="$failedSum"/></td>
<td><xsl:value-of select="$errorSum"/></td>
<td><xsl:value-of select="$failederrorSum"/></td>
<td></td>
</tr>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="testsuite">
<tr>
<td><xsl:value-of select="#name"/></td>
<td><xsl:value-of select="#tests"/></td>
<td><xsl:value-of select="#tests - (#failures + #errors)"/></td>
<td><xsl:value-of select="#failures"/></td>
<td><xsl:value-of select="#errors"/></td>
<td><xsl:value-of select="#failures + #errors"/></td>
<td> </td>
</tr>
</xsl:template>
<xsl:template name="testsSum">
<xsl:param name="numTests"/>
<xsl:choose>
<xsl:when test="$numTests">
<xsl:variable name="recursive_result">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="$numTests[position() > 1]"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="number($numTests[1]) + $recursive_result"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Gives this output:
<html>
<body>
<h2>Test Results</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Test Area</th>
<th>Total</th>
<th>Pass</th>
<th>Fail</th>
<th>Error</th>
<th>Fail and Error</th>
</tr>
<tr>
<td>test_ui_orchestration</td>
<td>10</td>
<td>10</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>
</td>
</tr>
<tr>
<td>test_ui_tables</td>
<td>13</td>
<td>12</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>
</td>
</tr>
<tr>
<td>test_ui_dashboard</td>
<td>18</td>
<td>15</td>
<td>3</td>
<td>0</td>
<td>3</td>
<td>
</td>
</tr>
<tr>
<td>
<b>All Tests</b>
</td>
<td>41</td>
<td>37</td>
<td>3</td>
<td>1</td>
<td>4</td>
<td/>
</tr>
</table>
</body>
</html>
Which looks like this:
Unfortunately, you've only posted fragments of your XSLT, but I believe the problem is that you are calling testsuite/#failures from the testsuites element.
Rather than returning a number, this is instead returning either a result tree fragment or node-set with the values of the failures attribute for each testsuite element.
As such the traditional numeric operations are failing, as they expect a number and get an RTF or nodeset instead, and attempt to apply the operation giving an unexpected result.
Without knowing what the rest of the code is, I'd suggest the way to fix is to wrap the variable instantiations either in a for-each loop, or even better, in a template that matches on an individual testsuite.

How to link Screenshot images to Testcase results in JUNIT report? [duplicate]

I am using the ant tasks 'junit' and 'junitreport' to run my JUnit Tests and generate a report at the end (=> "Unit Test Results").
Is it there some easy way to extend this output somehow to get more information displayed in the report? For example to add an additional column which contains link to a screenshot taken by the test.
I've seen that one could write an own ant junit test runner like the EclipseTestRunner
but this is quite some effort. Is there no API to access the elements of a unit report?
The junitreport task uses XSLT to produce the report from the XML files generated by the junittask.
You can customize the output by specifying your own XSLT using the styledir attribute of the nested report element:
<!-- use reportstyle/junit-frames.xsl to produce the report -->
<report styledir="reportstyle" format="frames" todir="testreport"/>
For customizing the the output, one option would be to make a copy of the default XSLT and modify that. Or you could look for an alternative XSLT which is more easy to customize for your purposes.
For small changes, it might be easiest to just import the default XSLT and override whatever templates you need to customize. For example, to add a column for each test, you would need to override the template which produces the table header and the template which produces a table row. Below, I have just copied those templates and modified them a bit to add one column (look for two additions marked with <!-- ADDED -->).
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- import the default stylesheet -->
<xsl:import href="jar:file:lib/ant-junit.jar!/org/apache/tools/ant/taskdefs/optional/junit/xsl/junit-frames.xsl"/>
<!-- override the template producing the test table header -->
<xsl:template name="testcase.test.header">
<xsl:param name="show.class" select="''"/>
<tr valign="top">
<xsl:if test="boolean($show.class)">
<th>Class</th>
</xsl:if>
<th>Name</th>
<th>Status</th>
<th width="80%">Type</th>
<th nowrap="nowrap">Time(s)</th>
<!-- ADDED -->
<th>Screenshot</th>
</tr>
</xsl:template>
<!-- override the template producing a test table row -->
<xsl:template match="testcase" mode="print.test">
<xsl:param name="show.class" select="''"/>
<tr valign="top">
<xsl:attribute name="class">
<xsl:choose>
<xsl:when test="error">Error</xsl:when>
<xsl:when test="failure">Failure</xsl:when>
<xsl:otherwise>TableRowColor</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:variable name="class.href">
<xsl:value-of select="concat(translate(../#package,'.','/'), '/', ../#id, '_', ../#name, '.html')"/>
</xsl:variable>
<xsl:if test="boolean($show.class)">
<td><xsl:value-of select="../#name"/></td>
</xsl:if>
<td>
<a name="{#name}"/>
<xsl:choose>
<xsl:when test="boolean($show.class)">
<xsl:value-of select="#name"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="#name"/>
</xsl:otherwise>
</xsl:choose>
</td>
<xsl:choose>
<xsl:when test="failure">
<td>Failure</td>
<td><xsl:apply-templates select="failure"/></td>
</xsl:when>
<xsl:when test="error">
<td>Error</td>
<td><xsl:apply-templates select="error"/></td>
</xsl:when>
<xsl:otherwise>
<td>Success</td>
<td></td>
</xsl:otherwise>
</xsl:choose>
<td>
<xsl:call-template name="display-time">
<xsl:with-param name="value" select="#time"/>
</xsl:call-template>
</td>
<!-- ADDED -->
<td>
screenshot
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
Here's how the result looks like:
Also if you don't want to replace the main xsl file, you can copy xsl file into the project root folder, update it with your changes and finally edit your build.xml file adding styledir attribute:
<report styledir="." format="noframes" todir="${junit.output.dir}"/>
Awesome ans by Jukka. This is an extension to Jukka's answer as to how exactly you can link the screenshot
<!-- ADDED -->
<td>
screenshot
</td>
Instead of above snippet in Jukka's ans, here is how you can link the screenshots :
<!-- Added screenshot link for failed tests -->
<td>
<xsl:variable name="class.name">
<xsl:value-of select="translate(#classname,'.','/')"/>
</xsl:variable>
<xsl:variable name="junit.base">
<xsl:call-template name="path"><xsl:with-param name="path" select="../#package"/></xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="failure">
<xsl:value-of select="#name"/>
</xsl:when>
<xsl:when test="error">
<xsl:value-of select="#name"/>
</xsl:when>
</xsl:choose>
</td>
All you need to do after the junit report is generated is - copy all the screenshots from "selenium/screenshots/" directory right under junit_report directory.
The above code will add link only for failed tests. If you want it for all then modify code accordingly.