xslt dynamic / conditional apply-template in function of a variable? - xslt

I want to display two different XSLT transformations in function of what the user want. The entire XSL file is the same, except for one line.
This line should be as it
<xsl:template match="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1])]">
or as it
<xsl:template match="/data/peptides/peptide">
My first idea was to create two different .xsl files, and to apply them (javascript) in function of a variable value.
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslDoc);
xhtml = xsltProcessor.transformToFragment(xmlDoc,document);
However, it is just a line and I would like to maintain only one file. I would like to do something like this
<xsl:param name="variable"/>
<xsl:choose>
<xsl:when test="$variable = 0">
<xsl:template match="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1])]">
...
</xsl:template>
</xsl:when>
<xsl:otherwise>
<xsl:template match="/data/peptides/peptide">
...
</xsl:template>
</xsl:otherwise>
</xsl:choose>
But it does not work.
trying with the feature "mode" in the xsl:apply-templates, this code neither works
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
<xsl:param name="analysis" select="1"/>
<xsl:template match="/">
<root><name><xsl:value-of select="$analysis"/></name><xsl:apply-templates select="/data/proteins/protein"/></root>
</xsl:template>
<xsl:template match="/data/proteins/protein">
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]"/>
</xsl:template>
<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:apply-templates select="/data/peptides/peptide" mode="one"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="two"/>
</xsl:otherwise>
</xsl:choose>
<xsl:template match="/data/peptides/peptide" mode="one">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="/data/peptides/peptide[generate-id()=
generate-id(key('byAccSeq', concat(accession, '|', sequence))[1])]" mode="two">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
-> http://www.xsltcake.com/slices/sgWUFu/2
this code is not correct since xsl:choose cannot be a child of xsl:stylesheet
SOLVED (improved below), this is the code that makes what I wanted
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
<xsl:param name="analysis" select="1"/>
<xsl:template match="/">
<root>
<name>
<xsl:value-of select="$analysis"/>
</name>
<xsl:apply-templates select="/data/proteins/protein"/>
</root>
</xsl:template>
<xsl:template match="/data/proteins/protein">
<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="one"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="/data/peptides/peptide[accession=current()/accession]" mode="two"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/data/peptides/peptide" mode="one">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="/data/peptides/peptide" mode="two"/>
<xsl:template match="/data/peptides/peptide[generate-id()=
generate-id(key('byAccSeq', concat(accession, '|', sequence)))]" mode="two">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
IMPROVED: the final code is much easier to read with much less duplicated code
here
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="analysis" select="0"/>
<xsl:key name="byAcc" match="/data/peptides/peptide" use="accession" />
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
<xsl:template match="/">
<root>
<name>
<xsl:value-of select="$analysis"/>
</name>
<xsl:apply-templates select="/data/proteins/protein" />
</root>
</xsl:template>
<xsl:template match="/data/proteins/protein">
<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:apply-templates select="key('byAcc',accession)" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="key('byAcc',accession)[
generate-id()
=
generate-id(key('byAccSeq', concat(accession, '|', sequence)))]" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/data/peptides/peptide">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
I keep the apply-template calls because I need it, but without them (as in the original code, see comments) is even simpler.
Thanks again, not only for answering but for teaching XSLT :)

/data/peptides/peptide[
generate-id()
=
generate-id(
key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1]
)
]
and
/data/peptides/peptide[
generate-id()
=
generate-id(
key('byAccSeq', concat(protein_accessions/accession, '|', sequence))
)
]
are equivalent. generate-id() always returns the ID of the first node in the node-set that has been passed to it.
Therefore it does not make any difference whether you use generate-id(some-node-set) or generate-id(some-node-set[1]). You can use either one of the XSLT files.

Change from <xsl:template> to <xsl:apply-templates>
<xsl:apply-templates select="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence))[1])]" mode="type1" />
<xsl:apply-templates select="/data/peptides/peptide[generate-id()=generate-id(key('byAccSeq', concat(protein_accessions/accession, '|', sequence)))]" mode="type2" />
After that, write <xsl:template> for both or in one accordingly.
Modify your solution in shorter way:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="byAccSeq" match="/data/peptides/peptide" use="concat(accession, '|', sequence)"/>
<xsl:param name="analysis" select="2"/>
<xsl:template match="/">
<root>
<name>
<xsl:value-of select="$analysis"/>
</name>
<xsl:apply-templates select="data[peptides/peptide/accession=proteins/protein/accession]/peptides" />
</root>
</xsl:template>
<xsl:template match="peptides">
<xsl:choose>
<xsl:when test="$analysis=1">
<xsl:copy-of select="peptide"/>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="peptide[generate-id()=generate-id(key('byAccSeq', concat(accession, '|', sequence)))]">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I have tested my code as per your input XML & output. Now I can replicate the same.
On my code, still I can reduce some more lines of code
Replace <xsl:for-each> with below line:-
<xsl:copy-of select="peptide[generate-id()=generate-id(key('byAccSeq', concat(accession, '|', sequence)))]"/>
Check it out..

Related

XSLT to handle quotes and Pipe Delimited symbol

Experts, i need to write XSLT 1.0 code to remove the quotes for multiple conditions.
CASE1: Remove the double quotes
CASE2: Remove the double quotes + delete the PIPE symbol inside that double quotes (IF exist)
CASE3: Remove Single quote " from the input field.
Input:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ns0:Accounting xmlns:ns0="http://sample.com">
<Record>
<DRCR>"DR"</DRCR>
<GLREFERENCE>"TEST|CASE"</GLREFERENCE>
<GLVALUEDATE>EXAM"PLE</GLVALUEDATE>
<GLACCOUNTNUMBER>"1160</GLACCOUNTNUMBER>
<GLEXAMPLE>123</GLEXAMPLE>
<GLEXAMPLE1>EXTRACT|2021-06-16|2853|1308026.7500|1176</GLEXAMPLE1>
</Record>
</ns0:Accounting>
** Desired Output:**
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ns0:Accounting xmlns:ns0="http://sample.com">
<Record>
<DRCR>DR</DRCR>
<GLREFERENCE>TEST CASE</GLREFERENCE>
<GLVALUEDATE>EXAMPLE</GLVALUEDATE>
<GLACCOUNTNUMBER>1160</GLACCOUNTNUMBER>
<GLEXAMPLE>123</GLEXAMPLE>
<GLEXAMPLE1>EXTRACT|2021-06-16|2853|1308026.7500|1176</GLEXAMPLE1>
</Record>
</ns0:Accounting>
** XSLT I tried:**
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="process">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="process">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '"')">
<xsl:value-of select="substring-before($text, '"')"/>
<xsl:value-of select="translate(substring-before(substring-after($text, '"'), '"'), '|', '')"/>
<xsl:call-template name="process">
<xsl:with-param name="text" select="substring-after(substring-after($text, '"'), '"')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This XSLT not handling case 3, which has single quote in the input field. Please assist here..
Maybe something like this could work for you:
XSLT 1.0 (+ EXSLT node-set function)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:choose>
<xsl:when test="contains(., '"')">
<xsl:variable name="tokens">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="exsl:node-set($tokens)/token">
<xsl:choose>
<xsl:when test="(position()=1 or position()=last()) and last() > 1">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(., '|', '')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'"'"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:if test="$token">
<token>
<xsl:value-of select="$token"/>
</token>
</xsl:if>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Note that this does not check the parity of the quotation marks. Any vertical bar character that is both preceded and followed by a quotation mark will be removed. For example, an input of:
<EXAMPLE>abc|123"def|456"ghi|789"jkl|012</EXAMPLE>
will be transformed to:
<EXAMPLE>abc|123def456ghi789jkl|012</EXAMPLE>

get value of variable to another template

There are references I found in the internet about the passing of variable to other template. I tried to follow all the references but, I can't get the value that I need to populate. I have this xml file:
<Item>
<Test>
<ID>123345677</ID>
</Test>
<DisplayID>99884534</DisplayID>
</Item>
I need to populate MsgId element if the DisplayID is not null, else get value from the ID. My XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="ID">
<xsl:variable name="IDV" select="substring(.,0,35)"/>
<xsl:apply-templates select="DisplayID">
<xsl:with-param name="IDP" select="$IDV"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="DisplayID">
<xsl:param name="IDP"/>
<xsl:element name="MsgId">
<xsl:choose>
<xsl:when test=".!='' or ./*">
<xsl:value-of select="substring(.,0,35)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($IDP,0,35)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
The condition if DisplayID is not null is working, however, if I remove the value of DisplayID, there's no value getting from the ID. I don't know if I doing it correctly.
Your feedback is highly appreciated.
Please try this,
Demo for references : http://xsltransform.net/ejivdHb/16
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://locomotive/bypass/docx" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Item">
<xsl:element name="MsgId">
<xsl:choose>
<xsl:when test="DisplayID !='' ">
<xsl:value-of select="substring(DisplayID , 0 ,35)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring(Test/ID,0,35)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Since this is tagged XSLT 2.0, the match="Item" template from #TechBreak can be replaced by
<xsl:template match="Item">
<MsgId>
<xsl:value-of select="substring(
if (DisplayId != '')
then DisplayID
else Test/ID, 1 ,35)"/>
</MsgId>
</xsl:template>
(Note character counting starts from 1)

Transform the name of a node via a key-value list in XSLT

My Source XML sample looks like this.
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<cd>
<T>A Book</T>
<A>A Man</A>
<D>Today</D>
</cd>
</catalog>
While 'T' means Title,'A' means Author,'D' means Date.
The output I want to get looks like this:
Title:A Book. Author:A Man. Date:Today
According to Implementing Key Value Concept in XSLT,I find that I can wirite the XSLT like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:codes>
<code key="T" value="Title"/>
<code key="A" value="Author"/>
<code key="D" value="Date"/>
</my:codes>
<xsl:key name="kCodeByName" match="code" use="#key"/>
<xsl:template match="/">
<xsl:for-each select="catalog/cd/*">
<xsl:apply-templates select="."/>:<xsl:value-of select="."/>.
</xsl:for-each>
</xsl:template>
<xsl:template match= "node()[name() = document('')/*/my:codes/*/#key]">
<xsl:variable name="vCur" select="name()"/>
<xsl:for-each select="document('')">
<xsl:value-of select=
"key('kCodeByName', $vCur)/#value"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
But if I want to use
<xsl:apply-templates select="name()"/>:<xsl:value-of select="."/>.
rather than
<xsl:apply-templates select="."/>:<xsl:value-of select="."/>.
What should I change in XSLT?
name() is a function, not a node; you cannot apply templates to it or use it in a match pattern.
Why don't you do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="codes">
<code key="T" value="Title"/>
<code key="A" value="Author"/>
<code key="D" value="Date"/>
</xsl:variable>
<xsl:key name="kCodeByName" match="code" use="#key"/>
<xsl:template match="cd">
<xsl:apply-templates/>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="cd/*">
<xsl:variable name="vCur" select="name()"/>
<xsl:for-each select="document('')">
<xsl:value-of select="key('kCodeByName', $vCur)/#value"/>
</xsl:for-each>
<xsl:text>:</xsl:text>
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>. </xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Edit
If you prefer, you can change the last template to:
<xsl:template match="cd/*">
<xsl:call-template name="lookup">
<xsl:with-param name="key" select="name()"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text>. </xsl:text>
</xsl:if>
</xsl:template>
and add:
<xsl:template name="lookup">
<xsl:param name="key"/>
<xsl:for-each select="document('')">
<xsl:value-of select="key('kCodeByName', $key)/#value"/>
</xsl:for-each>
</xsl:template>

XSL transformation failed : Expected QName

when I am trying to transform an XMl using below stylesheet, the transformation is failing with error Expected QName. What exactly is missing in the styelesheet, pasted below?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp" exclude-result-prefixes="dp">
<xsl:output method="xml" indent="yes" version="1.0"/>
<xsl:variable name="origo-svc-augmented" select="'Y'"/>
<xsl:variable name="origo-svc-ns" select="'http://www.origoservices.com'"/>
<xsl:template match="node()">
<xsl:variable name="namespace" select="namespace-uri(.)"/>
<xsl:choose>
<!--xsl:when test="$namespace = ''
or ($origo-svc-augmented='Y' and $namespace=$origo-svc-ns)"-->
<xsl:when test="$origo-svc-augmented = 'N'">
<xsl:element name="{local-name()}">
<xsl:copy-of select="namespace::*[not(namespace-uri()=$origo-svc-ns)]"/>
<xsl:apply-templates select="#*|node()|comment()|processing-instruction()|text()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{local-name()}" namespace="{$namespace}">
<xsl:copy-of select="namespace::*"/>
<xsl:apply-templates select="#*|node()|comment()|processing-instruction()|text()"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
UPDATE: After applying the suggestion from the answer provided, the stylesheet is working fine, updated stylesheet is as below
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp" exclude-result-prefixes="dp">
<xsl:output method="xml" indent="yes" version="1.0"/>
<xsl:variable name="origo-svc-augmented" select="'Y'"/>
<xsl:variable name="origo-svc-ns" select="'http://www.origoservices.com'"/>
<xsl:template match="*">
<xsl:variable name="namespace" select="namespace-uri(.)"/>
<xsl:choose>
<!--xsl:when test="$namespace = ''
or ($origo-svc-augmented='Y' and $namespace=$origo-svc-ns)"-->
<xsl:when test="$origo-svc-augmented = 'Y'">
<xsl:element name="{local-name()}">
<xsl:copy-of select="namespace::*[not(namespace-uri()=$origo-svc-ns)]"/>
<xsl:apply-templates select="#*|node()|comment()|processing-instruction()|text()"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{local-name()}" namespace="{$namespace}">
<xsl:copy-of select="namespace::*"/>
<xsl:apply-templates select="#*|node()|comment()|processing-instruction()|text()"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*|comment()|processing-instruction()|text()">
<xsl:copy>
<xsl:apply-templates select="#*|node()|comment()|processing-instruction()|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I haven't tried it, but I think it may be because you're matching on node() (i.e. element, text node, processing instruction comment), but then trying to create an element with the name of that possibly non-element node. QName refers to a valid XML name, and I presume a text node, for example, wouldn't have one (when you call local-name()). You could change your template match to be *, and add another template for the other types.

Create a recursive string function for adding a sequence

I've a challenging problem and so far I wasn't able to solve.
Within my xlst I have variable which contains a string.
I need to add the following sequence [eol] to this string.
On a fix position namely every 65 characters
I thought to use a function or template to recursive add this charackter.
The reason is that the string length can variate in length.
<xsl:function name="funct:insert-eol" as="xs:string" >
<xsl:param name="originalString" as="xs:string?"/>
<xsl:variable name="length">
<xsl:value-of select="string-length($originalString)"/>
</xsl:variable>
<xsl:variable name="start" as="xs:integer">
<xsl:value-of select="1"/>
</xsl:variable>
<xsl:variable name="eol" as="xs:integer">
<xsl:value-of select="65"/>
</xsl:variable>
<xsl:variable name="newLines">
<xsl:value-of select="$length idiv number('65')"/>
</xsl:variable>
<xsl:for-each select="1 to $newLines">
<xsl:value-of select="substring($originalString, $start, $eol)" />
</xsl:for-each>
</xsl:function>
The more I write code the more variables I need to introduce. This is still my lack on understanding.
For example we want every 5 chars an [eol]
aaaaaaabbbbbbccccccccc
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]cc
Hope someone has a starting point for me..
Regards Dirk
Rather straight-forward and short -- no recursion is necessary (and can even be specified as a single XPath expression):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="pLLength" select="5"/>
<xsl:template match="/*">
<xsl:variable name="vText" select="string()"/>
<xsl:for-each select="1 to string-length($vText) idiv $pLLength +1">
<xsl:value-of select="substring($vText, $pLLength*(position()-1)+1, $pLLength)"/>
<xsl:if test=
"not(position() eq last()
or position() eq last() and string-length($vText) mod $pLLength)">[eol]</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on this XML document:
<t>aaaaaaabbbbbbccccccccc</t>
the wanted, correct result is produced:
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]cc
When this XML document is processed:
<t>aaaaaaabbbbbbcccccccccddd</t>
again the wanted, correct result is produced:
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]ccddd[eol]
You can treat it as a grouping problem, using for-each-group:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:function name="mf:insert-eol" as="xs:string">
<xsl:param name="input" as="xs:string"/>
<xsl:param name="chunk-size" as="xs:integer"/>
<xsl:value-of>
<xsl:for-each-group select="string-to-codepoints($input)" group-by="(position() - 1) idiv $chunk-size">
<xsl:if test="position() gt 1"><xsl:sequence select="'eol'"/></xsl:if>
<xsl:sequence select="codepoints-to-string(current-group())"/>
</xsl:for-each-group>
</xsl:value-of>
</xsl:function>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text">
<xsl:copy>
<xsl:sequence select="mf:insert-eol(., 5)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
That stylesheet transforms
<root>
<text>aaaaaaabbbbbbccccccccc</text>
</root>
into
<root>
<text>aaaaaeolaabbbeolbbbcceolccccceolcc</text>
</root>
Try this one:
<?xml version='1.0' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name="TextToChange" select="'aaaaaaabbbbbbccccccccc'"/>
<xsl:param name="RequiredLength" select="xs:integer(5)"/>
<xsl:template match="/">
<xsl:call-template name="AddText"/>
</xsl:template>
<xsl:template name="AddText">
<xsl:param name="Text" select="$TextToChange"/>
<xsl:param name="TextLength" select="string-length($TextToChange)"/>
<xsl:param name="start" select="xs:integer(1)"/>
<xsl:param name="end" select="$RequiredLength"/>
<xsl:choose>
<xsl:when test="$TextLength gt $RequiredLength">
<xsl:value-of select="substring($Text,$start,$end)"/>
<xsl:text>[eol]</xsl:text>
<xsl:call-template name="AddText">
<xsl:with-param name="Text" select="substring-after($Text, substring($Text,$start,$end))"/>
<xsl:with-param name="TextLength"
select="string-length(substring-after($Text, substring($Text,$start,$end)))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>