Create a recursive string function for adding a sequence - xslt

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>

Related

How to pass a collection of nodes as a param of an XSL template?

This is my template (a simplification of a real situation):
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args/*">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
I call it like this:
<xsl:template match="f">
<xsl:call-template name="i">
<xsl:with-param name="args"/>
<a><xsl:value-of select="./#one"/></a>
<a><xsl:value-of select="./#two"/></a>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
Basically, I create artificial nodes <a/>, which looks ugly to me. Is there a better way?
My example would use functions but I have done both:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="mf:f1" as="text()">
<xsl:param name="items" as="item()*"/>
<xsl:value-of select="$items"/>
</xsl:function>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="foo">
<function-example>
<xsl:sequence select="mf:f1((#one, #two))"/>
</function-example>
<template-example1>
<xsl:call-template name="h">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</template-example1>
<template-example2>
<xsl:call-template name="i">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</template-example2>
</xsl:template>
<xsl:template name="h">
<xsl:param name="args"/>
<xsl:value-of select="$args"/>
</xsl:template>
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
transforms
<foo one="1" two="2"/>
into
<function-example>1 2</function-example>
<template-example1>1 2</template-example1>
<template-example2>12</template-example2>
https://xsltfiddle.liberty-development.net/jyfAiD9
Of course, for the sole xsl:value-of use, if you don't want to have the default space separator between the args values, use xsl:value-of separator="".
Consider the following simple example:
XML
<f one="alpha" two="bravo"/>
template call
<xsl:template match="f">
<xsl:call-template name="i">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</xsl:template>
template execution
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
Demo: https://xsltfiddle.liberty-development.net/93PXKr2

Is it possible to provide a few values for the same param in XSL (vararg)?

I'm trying to create an XSL 2.0 template, which I would be able to call with a few <xsl:with-param/> values, all of them bound to the same <xsl:param>. In other words, I'm looking for a variadic template or its best alternative. Can you suggest some?
ps. This is what I came up with:
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args/*">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
Then, I'm calling it like this:
<xsl:template match="f">
<xsl:call-template name="i">
<xsl:with-param name="args"/>
<a><xsl:value-of select="./#one"/></a>
<a><xsl:value-of select="./#two"/></a>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
There are many ways to do this, below are just three of them:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pParams" select="3,5,15,22,7"/>
<xsl:param name="pParams2" select="'3,5,15,22,7'"/>
<xsl:param name="pParams3" select="/*/*[1], /*/*[2], /*/*[3] "/>
<xsl:template match="/*">
<xsl:for-each select="$pParams">
<xsl:sequence select=". * 2"/>
</xsl:for-each>
<xsl:value-of select="'
'"/>
<xsl:for-each select="tokenize($pParams2, ',')">
<xsl:sequence select="number(.) * 2"/>
</xsl:for-each>
<xsl:value-of select="'
'"/>
<xsl:for-each select="$pParams3">
<xsl:sequence select="name(.)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on this XML document:
<t>
<a/>
<b/>
<c/>
</t>
the wanted result is produced:
6 10 30 44 14
6 10 30 44 14
a b c

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>

multiple concatenation the alphabet if it is not available in the variable 1 and available in variable 2

I have two variables in the xslt for example named var1 and var2.
Var1 = A,B,C,D,F G,H Var2 = L,M,F,C,D,K
I want to check that if any alphabet is not present in Var1 and present in Var2 ,append this alphabet after the Var1.
Means eventually i want below value stored in to var1
A,B,C,D,F,G,H,L,M,K
I am thinking something like below which is not working -
<xsl:variable name="Var1">
<xsl:if test="not(contains($va1,'L'))">
<xsl:value-of select="concat($va1, 'L')"/>
</xsl:if>
<xsl:if test="not(contains($va1,'M'))">
<xsl:value-of select="concat($va1, 'M')"/>
</xsl:if>
</xsl:variable>
NOTE - I am XSLT beginner and above code is just something in my head only and not tried so do not go with down vote please.
It's possible to some extent, but , are not preserved for $var2:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="var1">
<xsl:text>A,B,C,D,F,G,H</xsl:text>
</xsl:variable>
<xsl:variable name="var2">
<xsl:text>L,M,F,C,D,K</xsl:text>
</xsl:variable>
<xsl:variable name="merged" select="concat(concat($var1, ','), translate($var2, $var1, ''))"/>
<xsl:template match="#* | node()">
<xsl:apply-templates select="/"/>
</xsl:template>
<xsl:template match="/">
<xsl:value-of select="$merged"/>
</xsl:template>
</xsl:stylesheet>
Output:
A,B,C,D,F,G,H,LMK
Note that with XSLT 1.0, it's only possible for single characters like in your example (translate does not replace substrings!).
If you don't need the merged characters in a variable, you could do this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="sep" select="','"/>
<xsl:variable name="var1">
<xsl:text>A,B,C,D,F,G,H</xsl:text>
</xsl:variable>
<xsl:variable name="var2">
<xsl:text>L,M,F,C,D,K</xsl:text>
</xsl:variable>
<xsl:template match="#* | node()">
<xsl:apply-templates select="/"/>
</xsl:template>
<xsl:template match="/">
<xsl:value-of select="$var1"/>
<xsl:value-of select="$sep"/>
<xsl:call-template name="not-contained">
<xsl:with-param name="string1" select="$var1"/>
<xsl:with-param name="string2" select="$var2"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="not-contained">
<xsl:param name="string1"></xsl:param>
<xsl:param name="string2"></xsl:param>
<xsl:param name="sep" select="$sep"/>
<xsl:param name="match"/>
<xsl:value-of select="$match"/>
<xsl:choose>
<xsl:when test="contains($string2, $sep)">
<xsl:variable name="before" select="substring-before($string2, $sep)"/>
<xsl:variable name="no-match" select="not(contains($string1, $before))"/>
<xsl:if test="$no-match">
<xsl:value-of select="$before"/>
</xsl:if>
<xsl:call-template name="not-contained">
<xsl:with-param name="string1" select="$string1"/>
<xsl:with-param name="string2" select="substring-after($string2, $sep)"/>
<xsl:with-param name="match">
<xsl:if test="$no-match">
<xsl:value-of select="$sep"/>
</xsl:if>
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:if test="not(contains($string1, $string2))">
<xsl:value-of select="$string2"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output:
A,B,C,D,F,G,H,L,M,K
If every letter was a text node in a source document, <xsl:key> could be used to group them together and thus eliminate duplicates.

Using a Map in XSL for expanding abbreviations

I saw a similar question on creating a Map.
That answer has this code:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="/">
<xsl:variable name="map">
<map>
<entry key="key-1">value1</entry>
<entry key="key-2">value2</entry>
<entry key="key-3">value3</entry>
</map>
</xsl:variable>
<output>
<xsl:value-of select="msxsl:node-set($map)/map/entry[#key='key-1']"/>
</output>
</xsl:template>
I would like to replace the output command to use a value in my XML to see if it is a key in the map and then replace it with the value.
Is the best way to do a for-each select on the map and compare with contains?
Here is a snippet of the XML:
<document>
<content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
SCREW - ADJUST
</content>
</document>
The content node value may have a string containing an abbreviation that I want to replace with the full value.
Thanks,
Paul
No need to use a for-each - this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output indent="yes" method="xml" />
<xsl:variable name="abbreviations">
<abbreviation key="Brkt Pivot R">Bracket Pivot R</abbreviation>
<abbreviation key="Foo">Expanded Foo</abbreviation>
<abbreviation key="Bar">Expanded Bar</abbreviation>
</xsl:variable>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:variable name="text" select="."/>
<xsl:variable name="abbreviation" select="msxsl:node-set($abbreviations)/*[#key=$text]"/>
<xsl:choose>
<xsl:when test="$abbreviation">
<xsl:value-of select="$abbreviation"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
will convert any XML in an exact copy expanding all the text matching an abbreviation defined in the abbreviations variable at the top.
If you want to expand the abbreviations only within specific elements you can modify the second template match="..." rule.
On the other hand if you want to expand ANY occurance of all the abbreviations within the text you need loops - that means recursion in XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output indent="yes" method="xml" />
<xsl:variable name="abbreviations">
<abbreviation key="Brkt">Bracket</abbreviation>
<abbreviation key="As">Assembly</abbreviation>
<abbreviation key="Foo">Expanded Foo</abbreviation>
<abbreviation key="Bar">Expanded Bar</abbreviation>
</xsl:variable>
<!-- Replaces all occurrences of a string with another within a text -->
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="from"/>
<xsl:param name="to"/>
<xsl:choose>
<xsl:when test="contains($text,$from)">
<xsl:value-of select="concat(substring-before($text,$from),$to)"/>
<xsl:call-template name="replace">
<xsl:with-param name="text" select="substring-after($text,$from)"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="to" select="$to"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Replace all occurences of a list of abbreviation with their expanded version -->
<xsl:template name="replaceAbbreviations">
<xsl:param name="text"/>
<xsl:param name="abbreviations"/>
<xsl:choose>
<xsl:when test="count($abbreviations)>0">
<xsl:call-template name="replaceAbbreviations">
<xsl:with-param name="text">
<xsl:call-template name="replace">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="from" select="$abbreviations[1]/#key"/>
<xsl:with-param name="to" select="$abbreviations[1]"/>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="abbreviations" select="$abbreviations[position()>1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="replaceAbbreviations">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="abbreviations" select="msxsl:node-set($abbreviations)/*"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Applying this second XSLT to
<document>
<content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
Brkt Pivot R
</content>
</document>
produces
<document>
<content name="PART_DESC_SHORT" type="text" vse-streams="2" u="22" action="cluster" weight="1">
Bracket Pivot R
</content>
</document>
Note that:
this solution assumes that no abbreviation ovelap (e.g. two separate abbreviations Brk and Brkt)
it uses XSLT 1.0 - a better solution is probably possible with XSLT 2.0
this kind of heavy string processing is likely quite inefficient in XSLT, it is probably better to write an extension function in some other language and call it from the XSLT.