Debugging Error in Xpath 2.0 expression [closed] - xslt

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
Problem in the cases in the below XSLt code, even though variables are defined, it shows undefined variable.
First choose is to test wheather the Year is for Leap year or non Leap Year.
Second choose only finds the cases for number of Days of the month.
<xsl:template name="CalDateNTime">
<xsl:param name="pDate"/>
<xsl:param name="pMonth"/>
<xsl:param name="pYear"/>
<xsl:choose>
<!-- Leap Year -->
<xsl:when test="($pYear mod 4=0 and $pYear mod 100 !=0) or $pYear mod 400 =0" >
<xsl:value-of select="$pDate"/>
<xsl:value-of select="$rDate"/>
<xsl:value-of select="$pMonth"/>
<xsl:choose>
<xsl:when test="$pMonth=1 and $rDate=32">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="2"/>
</xsl:when>
<xsl:when test="$pMonth=2 and $rDate=30">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="3"/>
</xsl:when>
<xsl:when test="$pMonth=3 and $rDate=32">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="4"/>
</xsl:when>
</xsl:choose>
</xsl:when>
<!-- Non - Leap Year -->
<xsl:otherwise>
<xsl:variable name="jan" select="31"/>
<xsl:variable name="feb" select="28"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSLT variables only exist within their parent.
<xsl:when test="$pMonth=1 and $rDate=32">
<xsl:variable name="rDate" select="1" />
<xsl:variable name="rMonth" select="2" />
</xsl:when>
These two variables are out of scope immediately (at the </xsl:when>).
You must structure your program differently. Put the <xsl:choose> into the variable.
I can only assume what your partial sample code is supposed to do. My assumption is: It should calculate the day after a given date.
Here's an alternative implementation:
<xsl:template name="CalDateNTime">
<xsl:param name="pDate" />
<xsl:param name="pMonth" />
<xsl:param name="pYear" />
<xsl:variable name="dayNum" select="'312831303130313130313031'" />
<xsl:variable name="isLeap" select="($pYear mod 4 = 0 and $pYear mod 100 != 0) or ($pYear mod 400 = 0)" />
<!-- determine index into the $dayNum string -->
<xsl:variable name="idx" select="($pMonth - 1) * 2" />
<xsl:variable name="maxDate" select="substring($dayNum, $idx + 1, 2)" />
<xsl:variable name="isSameMonth" select="($pDate < $maxDate) or ($pDate = $maxDate and $pMonth = 2 and $isLeap)" />
<!-- calculate following day, month, year values -->
<xsl:variable name="nDate">
<xsl:choose>
<xsl:when test="$isSameMonth"><xsl:value-of select="$pDate + 1" /></xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="nMonth">
<xsl:choose>
<xsl:when test="$isSameMonth"><xsl:value-of select="$pMonth" /></xsl:when>
<xsl:when test="$pMonth < 12"><xsl:value-of select="$pMonth + 1" /></xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="nYear">
<xsl:choose>
<xsl:when test="not($isSameMonth) and $nMonth = 1"><xsl:value-of select="$pYear + 1" /></xsl:when>
<xsl:otherwise><xsl:value-of select="$pYear" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- build output string -->
<xsl:value-of select="concat($nYear, '-')" />
<xsl:if test="$nMonth < 10">0</xsl:if>
<xsl:value-of select="concat($nMonth, '-')" />
<xsl:if test="$nDate < 10">0</xsl:if>
<xsl:value-of select="$nDate" />
</xsl:template>
Usage
<xsl:call-template name="CalDateNTime">
<xsl:with-param name="pDate" select="28" />
<xsl:with-param name="pMonth" select="2" />
<xsl:with-param name="pYear" select="2000" />
</xsl:call-template>
Result
2000-02-29

Related

Sum value of output of template in Apache FOP

I am using Apache FOP to generate a PDF document, and to display a certain value I have to iterate over a number of nodes to determine a total price value, then sum that value. So far I have a function that iterates over an array and then retrieves the intended value, but the issue occurs when I try to sum the results.
<xsl:function name="foo:buildTotalValue">
<xsl:param name="items" />
<xsl:variable name="totals">
<xsl:for-each select="$items/charge">
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="sum(exsl:node-set($totals))" />
</xsl:function>
<xsl:template name="getTotalPriceNode">
<xsl:param name="itemParam" />
<xsl:choose>
<xsl:when test="$itemParam/Recurrance = 'OnceOff'">
<xsl:value-of select="$itemParam/TotalValue" />
</xsl:when>
<xsl:when test="$itemParam/Recurrance = 'Monthly'">
<xsl:value-of select="$itemParam/TotalValue * $itemParam/Months"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
I'm hoping that when I pass in foo:buildTotalValue with entries like this:
<Charges>
<Charge>
<Recurrance>OnceOff</Recurrance>
<TotalValue>50.00</TotalValue>
</Charge>
<Charge>
<Recurrance>Monthly</Recurrance>
<TotalValue>10.00</TotalValue>
<Months>6</Months>
</Charge>
</Charges>
would return with the value 110.00, but instead I get the error:
Cannot convert string "50.0060.00" to double
I've tried adding a <value> or something in the templates and then using that as a selector for the exsl:node-set function but it doesn't seem to make a difference.
AFAICT, the problem with your function is that it builds a concatenated string of values returned by the called template, instead of a tree of nodes that can be converted into a node-set and summed.
Try changing:
<xsl:for-each select="$items/charge">
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</xsl:for-each>
to:
<xsl:for-each select="$items/charge">
<total>
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</total>
</xsl:for-each>
and:
<xsl:value-of select="sum(exsl:node-set($totals))" />
to:
<xsl:value-of select="sum(exsl:node-set($totals)/total)" />
Untested, because (see comment to your question).
I ended up using the suggestion from Martin from the comment - the xpath 2+ expression along the line of:
sum(Charge[Recurrance = 'OnceOff']/TotalValue | Charge[Recurrance = 'Monthly']/(TotalValue * Months))
which was able to achieve what I needed without the use of functions / templates / node-set (And in a lot less code)

extract csv content from an xml tag

i have a source xml as below
<rest-adapter-response>
<metadata>
<status>success</status>
</metadata>
<status-line>
<code>200</code>
<reason>OK</reason>
</status-line>
<header-lines>
<Cache-Control>private, max-age=0</Cache-Control>
<Transfer-Encoding>chunked</Transfer-Encoding>
<Content-Type>application/octet-stream</Content-Type>
<Expires>Thu, 25 Apr 2019 08:51:55 GMT</Expires>
<Last-Modified>Fri, 10 May 2019 08:51:55 GMT</Last-Modified>
<Server>Microsoft-IIS/10.0</Server>
<X-SharePointHealthScore>1</X-SharePointHealthScore>
<X-SP-SERVERSTATE>ReadOnly=0</X-SP-SERVERSTATE>
<DATASERVICEVERSION>3.0</DATASERVICEVERSION>
<X-Download-Options>noopen</X-Download-Options>
<Content-Disposition>attachment</Content-Disposition>
<SPClientServiceRequestDuration>224</SPClientServiceRequestDuration>
<X-AspNet-Version>4.0.30319</X-AspNet-Version>
<SPRequestGuid>de31db9e-70cb-8000-7fba-6c3e85d9c810</SPRequestGuid>
<request-id>de31db9e-70cb-8000-7fba-6c3e85d9c810</request-id>
<MS-CV>ntsx3stwAIB/umw+hdnIEA.0</MS-CV>
<Strict-Transport-Security>max-age=31536000</Strict-Transport-Security>
<X-FRAME-OPTIONS>SAMEORIGIN</X-FRAME-OPTIONS>
<X-Powered-By>ASP.NET</X-Powered-By>
<MicrosoftSharePointTeamServices>16.0.0.8824</MicrosoftSharePointTeamServices>
<X-Content-Type-Options>nosniff</X-Content-Type-Options>
<X-MS-InvokeApp>1; RequireReadOnly</X-MS-InvokeApp>
<P3P>CP="ALL IND DSP COR ADM CONo CUR CUSo IVAo IVDo PSA PSD TAI TELo OUR SAMo CNT COM INT NAV ONL PHY PRE PUR UNI"</P3P>
<Date>Fri, 10 May 2019 08:51:55 GMT</Date>
</header-lines>
<message-body>
<non-xml-data-response>COA,COA Acct Desc,Acct Prefix,Revaluation Acct,Mapping Changes - Additions( A ) Deletions ( D ) Changes ( C ),MJE,OIM Recon,Comments for difference:10000274,"Citibank, Operating, USD, 31165975",1000,10009999,A,,X,10000374,"Citibank, Clearing, USD, 31165975",1000,10009999,A,,X,10006604,"HSBC, Operating, SAR, SA0345000000003179660002",1000,10009999,A,,X,10006605,"Citibank, Operating, ZAR, 0202099009",1000,10009999,A,,X,123,,,456,,,,</non-xml-data-response>
</message-body>
</rest-adapter-response>
above XML is the response of a share point web service which tried to read a csv file and gave a response like this. as you can see in the above response xml , csv data did come but inside one xmltag called <message-body> , and also lost the new line after every row format!!
now i need to recreate the csv !!. and the worst part is , in the tool where i receive this format i have capability to write xslt and xml ! no hosting language code or libraries could be used.
also only xslt 1.0.
there is a question like this
question on creating csv from xml , but this is bit different from my requirement . am just learning xslt and xpath , can any one help me in this ?
below is the requested output :
click here to view the csv format
If one makes the assumption that the header row is terminated by a colon and that there are 7 values in each data row*, then it's possible to use the following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/rest-adapter-response">
<xsl:variable name="csv" select="message-body/non-xml-data-response" />
<!-- header -->
<xsl:value-of select="substring-before($csv, ':')" />
<xsl:text>:
</xsl:text>
<!-- data -->
<xsl:call-template name="restore-csv">
<xsl:with-param name="text" select="substring-after($csv, ':')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="restore-csv">
<xsl:param name="text"/>
<xsl:param name="i" select="1"/>
<xsl:choose>
<xsl:when test="contains($text, ',')">
<xsl:variable name="value">
<xsl:choose>
<xsl:when test="starts-with($text, '"')">
<xsl:text>"</xsl:text>
<xsl:value-of select="substring-before(substring-after($text, '"'), '"')"/>
<xsl:text>"</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-before($text, ',')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- output -->
<xsl:value-of select="$value"/>
<xsl:choose>
<xsl:when test="$i mod 7 = 0">
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>,</xsl:text>
</xsl:otherwise>
</xsl:choose>
<!-- recursive call -->
<xsl:call-template name="restore-csv">
<xsl:with-param name="text">
<xsl:choose>
<xsl:when test="starts-with($text, '"')">
<xsl:value-of select="substring-after(substring-after($text, '"'), '",')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-after($text, ',')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
Result
COA,COA Acct Desc,Acct Prefix,Revaluation Acct,Mapping Changes - Additions( A ) Deletions ( D ) Changes ( C ),MJE,OIM Recon,Comments for difference:
10000274,"Citibank, Operating, USD, 31165975",1000,10009999,A,,X
10000374,"Citibank, Clearing, USD, 31165975",1000,10009999,A,,X
10006604,"HSBC, Operating, SAR, SA0345000000003179660002",1000,10009999,A,,X
10006605,"Citibank, Operating, ZAR, 0202099009",1000,10009999,A,,X
123,,,456,,,
This may need more work to handle possible escaped double-quotes within quoted values.
--
(*) The strange thing here is that there are 8 values in the header row, but only 7 in the data rows.

XSLT 1.0 recursion

I'm stuck with recursion, was wondering if anyone can help me out with it.
I have <Receipts> and <Deposits> elements, that are not verbose, i.e. that a <Receipts> element doesn't have an attribute to show what <Deposit> it is towards. I need to figure out <Deposits> "still amount due" and when a last receipt towards it was paid if any.
I'm trying to do it with the following code:
The idea was to take 1st deposit and see if there are receipts. If the deposit isn't fully paid and there are more receipts - call that recorsive function with all the same params except now count in following receipt.
If there aren't any more receipts or deposit is payed - process it correctly (add required attributes). Otherwise proceed with 2nd deposit. And so on.
However, the XSLT crashes with error message that "a processor stack has overflowed - possible cause is infinite template recursion"
I would really appreciate any help/teps... I'm not that great with recursion and can't understand why mine here doesn't work.
Thanks! :)
<!-- Accumulate all the deposits with #DueAmount attribute -->
<xsl:variable name="depositsClassified">
<xsl:call-template name="classifyDeposits">
<!-- a node-list of all Deposits elements ordered by DueDate Acs -->
<xsl:with-param name="depositsAll" select="$deposits"/>
<xsl:with-param name="depositPrevAmount" select="'0'"/>
<!-- a node-list of all Receipts elements ordered by ReceivedDate Acs -->
<xsl:with-param name="receiptsAll" select="$receiptsAsc"/>
<xsl:with-param name="receiptCount" select="'1'"/>
</xsl:call-template>
</xsl:variable>
<xsl:template name="classifyDeposits">
<xsl:param name="depositsAll"/>
<xsl:param name="depositPrevAmount" select="'0'"/>
<xsl:param name="receiptsAll"/>
<xsl:param name="receiptCount"/>
<xsl:if test="$depositsAll">
<!-- Do required operations for the 1st deposit -->
<xsl:variable name="depositFirst" select="$depositsAll[1]"/>
<xsl:variable name="receiptSum">
<xsl:choose>
<xsl:when test="$receiptsAll">
<xsl:value-of select="sum($receiptsAll[position() <= $receiptCount]/#ActionAmount)"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="diff" select="$depositPrevAmount + $depositFirst/#DepositTotalAmount - $receiptSum"/>
<xsl:choose>
<xsl:when test="$diff > 0 and
$receiptCount < $receiptsQuantityOf">
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$depositsAll"/>
<xsl:with-param name="depositPrevAmount" select="$depositPrevAmount"/>
<xsl:with-param name="receiptsAll" select="$receiptsAll"/>
<xsl:with-param name="receiptCount" select="$receiptCount + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- Record changes to the deposit (#DueAmount and receipt ReceivedDate) -->
<xsl:apply-templates select="$depositFirst" mode="defineDeposit">
<xsl:with-param name="diff" select="$diff"/>
<xsl:with-param name="latestReceiptForDeposit" select="$receiptsAll[position() = $receiptCount]"/>
</xsl:apply-templates>
<!-- Recursive call to the next deposit -->
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$depositsAll[position() > 1]"/>
<xsl:with-param name="depositPrevAmount" select="$depositPrevAmount + $depositFirst/#DepositTotalAmount"/>
<xsl:with-param name="receiptsAll" select="$receiptsAll"/>
<xsl:with-param name="receiptCount" select="'1'"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<!-- Determine deposit's status, due amount and payment received date if any -->
<xsl:template match="Deposits" mode="defineDeposit">
<xsl:param name="diff"/>
<xsl:param name="latestReceiptForDeposit"/>
<xsl:choose>
<xsl:when test="$diff <= 0">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'paid'"/>
<xsl:with-param name="dueAmount" select="'0'"/>
<xsl:with-param name="receipt" select="$latestReceiptForDeposit"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$diff = ./#DepositTotalAmount">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'due'"/>
<xsl:with-param name="dueAmount" select="$diff"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$diff < ./#DepositTotalAmount">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'outstanding'"/>
<xsl:with-param name="dueAmount" select="$diff"/>
<xsl:with-param name="receipt" select="$latestReceiptForDeposit"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
<xsl:template match="Deposits" mode="addAttrs">
<xsl:param name="status"/>
<xsl:param name="dueAmount"/>
<xsl:param name="receipt" select="''"/>
<!-- Constract a new MultiDeposits element with required info -->
<xsl:copy>
<xsl:copy-of select="./#*"/>
<xsl:attribute name="Status"><xsl:value-of select="$status"/></xsl:attribute>
<xsl:attribute name="DueAmount"><xsl:value-of select="$dueAmount"/></xsl:attribute>
<xsl:if test="$receipt">
<xsl:attribute name="latestReceiptDate">
<xsl:value-of select="$receipt/#ActionDate"/>
</xsl:attribute>
</xsl:if>
<xsl:copy-of select="./*"/>
</xsl:copy>
</xsl:template>
Your named template classifyDeposits is definitely recursing infinitely. It has no base case. In the <xsl:choose> section you have two conditions: <xsl:when> and <xsl:otherwise>, however you call classifyDeposits in both conditions.
So no matter what you pass to this template, it will call itself. You need to have a base case: a condition in which the template doesn't call itself.
In pseudocode, you're essentially doing this:
function recursive
if (A>B) then
call recursive
else
call recursive
You need to add another branch, somewhere, that does not call itself.
function recursive
if (C=0) then
return
else if (A>B) then
call recursive
else
call recursive
Of course, just having the condition isn't good enough. It needs to be reachable.
One thing i've learned about concerning recursion is that you need to make sure that you have a condition that will trigger when you are done.
example:
decrement_me_until_zero(int i)
{
if(i ==0) return; //we're done here, roll on back up!
else
{
i = i-1;
decrement_me_until_zero(i);
return;
}
}
I don't know xlst at all, but this can be a simple reason why recursion can go into an infinite loop. Consider instead:
decrement_me_until_zero(int i)
{
if(i ==0) return; //we're done here, roll on back up!
else
{
decrement_me_until_zero(i);
i=i-1; //oops! we decrement after we pass the variable down!
return;
}
}
I hope that helps

XSLT Paging - default to current Date

I am using an xslt transform to show a long list of events.
It has paging, but what I would like is for it to default to the first events that are closest to the current date.
I'm assuming you have a useful date format (YYYY-MM-DD).
<xsl:param name="currentDate" select="''" /><!-- fill this from outside! -->
<xsl:template name="isPageSelected"><!-- returns true or false -->
<xsl:param name="eventsOnPage" /><!-- expects a node-set of events -->
<xsl:choose>
<xsl:when test="$eventsOnPage">
<!-- create a string "yyyy-mm-dd,YYYY-MM-DD-" (note the trailing dash) -->
<xsl:variable name="dateRange">
<xsl:for-each select="$eventsOnPage">
<xsl:sort select="date" />
<xsl:if test="position() = 1">
<xsl:value-of select="concat(date, ',')" />
</xsl:if>
<xsl:if test="position() = last()">
<xsl:value-of select="concat(date, '-')" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- trailing dash ensures that the "less than" comparison succeeds -->
<xsl:value-of select="
$currentDate >= substring-before($dateRange, ',')
and
$currentDate < substring-after($dateRange, ',')
" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="false()" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
So in your paging routine, to find out if the current page is the selected one, invoke
<xsl:variable name="isPageSelected">
<xsl:call-template name="isPageSelected">
<xsl:with-param name="eventsOnPage" select="event[whatever]" />
</xsl:call-template>
</xsl:variable>
<!-- $isPageSelected now is true or false, proceed accordingly -->
Since sorting in XSLT 1.0 is horribly, horribly broken, your best bet is to find an extension or include a Unix-style time somewhere in your source XML so you can sort on that (although an ISO-formatted string might also work).

XSL Rookie -Can I make an XSL that matches "this phrase" also match "ths fraze" without repeating the entire "if test" block?

I'm a complete XSL novice, writing an XSL file to format lots of different error messages that can appear in the output log created by an application into CSV format.
Slight variations might occur in the matchable tags in these output logs. For example, one sentence in the log might contain the phrase "Service Month/Year:" but another one, from a different area of the application, would contain "Svc Month/Yr:" instead.
Is there a way to put both those variations of that phrase in a single line of my XSL? Or do I have to repeat the entire If block, with each variation in the phrase that I want to match on in its own If block?
I tried posting the XSL here surrounded by backticks, but it runs all together in one big lump that's impossible to read. If anyone can help with this question, I'm happy to post it if you tell me how to make it readable. :-)
Thank you.
XSL allows combining of conditional statements like other langues. Each one doesn't require its on if statement. Were you thinking something along these lines?
<xsl:choose>
<xsl:when test="contains(text(), 'Service Month/Year:')
or contains(text(), 'Svc Month/Yr:')
">
<!-- do something -->
</xsl:when>
</xsl:choose>
Keep in mind xml/xsl is case sensitive. To make it more flexible, it is even more verbose:
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:choose>
<xsl:when test="contains(translate(text(), $upper, $lower), 'service month/year:')
or contains(translate(text(), $upper, $lower), 'svc month/yr:')
">
<!-- do something -->
</xsl:when>
</xsl:choose>
EDIT: And an even better answer I whipped up
<xsl:template name="containsToken">
<xsl:param name="inputString"/>
<xsl:param name="tokens"/>
<xsl:param name="delimiter"/>
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:choose>
<xsl:when test="contains($tokens, $delimiter)">
<xsl:variable name="token">
<xsl:value-of select="substring-before($tokens, $delimiter)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains(translate($inputString, $upper, $lower), translate($token, $upper, $lower))">
<xsl:text>True</xsl:text>
</xsl:when>
<xsl:otherwise>
<!-- loop -->
<xsl:call-template name="containsToken">
<xsl:with-param name="inputString" select="$inputString"/>
<xsl:with-param name="tokens" select="substring-after($tokens, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains(translate($inputString, $upper, $lower), translate($tokens, $upper, $lower))">
<xsl:text>True</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>False</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Usage:
<xsl:variable name="found">
<xsl:call-template name="containsToken">
<xsl:with-param name="inputString" select="text()"/>
<xsl:with-param name="tokens" select="'Service Month/Year:|Svc Month/Yr:'"/>
<xsl:with-param name="delimiter" select="'|'"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$found = 'True'">
<!-- process -->
</xsl:if>
Delimiter can be whatever character or charcters you want. Tokens is a list of things to search for with the delimiter between each one. Enjoy!