File formating with XSLT

Discussion in 'XML' started by olivier.scalbert@algosyn.com, Jan 31, 2007.

  1. Guest

    Hello,

    >From the source file, I need to create with XSLT a result file with

    the following constraints:

    source:
    <Source>
    <Item>AAA</Item>
    <Item>BBBBBBBBBBB</Item>
    <Item>CCCCCCCCC</Item>
    <Item>DDDDDDDDDDDDDDD</Item>
    <Item>EEEEE</Item>
    <Item>FF</Item>
    <Item>G</Item>
    </Source>

    result: (text file with line length = 16, with spaces)
    AAA BBBBBBBBBBB
    CCCCCCCCC
    DDDDDDDDDDDDDDD
    EEEEE FF G

    Each line of the output file is composed of Item texts separated by
    one space.
    If by adding a new item text on the line we have more than 16
    characters then pad the current line with spaces and copy the item
    text to a new line.

    I do not know if it is possible to do that in XSLT.
    Thanks for your help.

    Olivier
    , Jan 31, 2007
    #1
    1. Advertising

  2. Andy Dingley Guest

    Andy Dingley, Jan 31, 2007
    #2
    1. Advertising

  3. Use the FXSL stylesheet file:

    strSplit-to-Lines.xsl

    And modify it appropriately for your case.

    This transformation (just modified the above stylesheet, calling the
    template with lineLength=16):

    <xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:f="http://fxsl.sf.net/"
    xmlns:str-split2lines-func="f:str-split2lines-func"
    exclude-result-prefixes="f str-split2lines-func"
    >


    <xsl:import href="str-foldl.xsl"/>

    <!-- to be applied on text.xml -->

    <str-split2lines-func:str-split2lines-func/>

    <xsl:eek:utput indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="/">
    <xsl:call-template name="str-split-to-lines">
    <xsl:with-param name="pStr"
    select="concat(normalize-space(/), ' ')"/>
    <xsl:with-param name="pLineLength" select="16"/>
    <xsl:with-param name="pDelimiters" select="'
    '"/>
    </xsl:call-template>
    </xsl:template>

    <xsl:template name="str-split-to-lines">
    <xsl:param name="pStr"/>
    <xsl:param name="pLineLength" select="60"/>
    <xsl:param name="pDelimiters" select="'
    '"/>

    <xsl:variable name="vsplit2linesFun"
    select="document('')/*/str-split2lines-func:*[1]"/>

    <xsl:variable name="vrtfParams">
    <delimiters><xsl:value-of select="$pDelimiters"/></delimiters>
    <lineLength><xsl:copy-of select="$pLineLength"/></lineLength>
    </xsl:variable>

    <xsl:variable name="vResult">
    <xsl:call-template name="str-foldl">
    <xsl:with-param name="pFunc" select="$vsplit2linesFun"/>
    <xsl:with-param name="pStr" select="$pStr"/>
    <xsl:with-param name="pA0" select="$vrtfParams"/>
    </xsl:call-template>
    </xsl:variable>

    <xsl:for-each select="$vResult/line">
    <xsl:for-each select="word">
    <xsl:value-of select="concat(., ' ')"/>
    </xsl:for-each>
    <xsl:value-of select="'
    '"/>
    </xsl:for-each>
    </xsl:template>

    <xsl:template match="str-split2lines-func:*" mode="f:FXSL">
    <xsl:param name="arg1" select="/.."/>
    <xsl:param name="arg2"/>

    <xsl:copy-of select="$arg1/*[position() &lt; 3]"/>
    <xsl:copy-of select="$arg1/line[position() != last()]"/>

    <xsl:choose>
    <xsl:when test="contains($arg1/*[1], $arg2)">
    <xsl:if test="string($arg1/word)">
    <xsl:call-template name="fillLine">
    <xsl:with-param name="pLine" select="$arg1/line[last()]"/>
    <xsl:with-param name="pWord" select="$arg1/word"/>
    <xsl:with-param name="pLineLength" select="$arg1/*[2]"/>
    </xsl:call-template>
    </xsl:if>
    </xsl:when>
    <xsl:eek:therwise>
    <xsl:copy-of select="$arg1/line[last()]"/>
    <word><xsl:value-of select="concat($arg1/word, $arg2)"/></word>
    </xsl:eek:therwise>
    </xsl:choose>
    </xsl:template>

    <!-- Test if the new word fits into the last line -->
    <xsl:template name="fillLine">
    <xsl:param name="pLine" select="/.."/>
    <xsl:param name="pWord" select="/.."/>
    <xsl:param name="pLineLength" />

    <xsl:variable name="vnWordsInLine" select="count($pLine/word)"/>
    <xsl:variable name="vLineLength" select="string-length($pLine) +
    $vnWordsInLine"/>
    <xsl:choose>
    <xsl:when test="not($vLineLength + string-length($pWord) >
    $pLineLength)">
    <line>
    <xsl:copy-of select="$pLine/*"/>
    <xsl:copy-of select="$pWord"/>
    </line>
    </xsl:when>
    <xsl:eek:therwise>
    <xsl:copy-of select="$pLine"/>
    <line>
    <xsl:copy-of select="$pWord"/>
    </line>
    <word/>
    </xsl:eek:therwise>
    </xsl:choose>
    </xsl:template>

    </xsl:stylesheet>

    Produces the wanted results (the lines are truncated but padding each line
    to 16 chars width is left as an exercise to the reader :eek:) )

    AAA BBBBBBBBBBB
    CCCCCCCCC
    DDDDDDDDDDDDDDD
    EEEEE FF G

    Cheers,
    Dimitre Novatchev.


    <> wrote in message
    news:...
    > Hello,
    >
    >>From the source file, I need to create with XSLT a result file with

    > the following constraints:
    >
    > source:
    > <Source>
    > <Item>AAA</Item>
    > <Item>BBBBBBBBBBB</Item>
    > <Item>CCCCCCCCC</Item>
    > <Item>DDDDDDDDDDDDDDD</Item>
    > <Item>EEEEE</Item>
    > <Item>FF</Item>
    > <Item>G</Item>
    > </Source>
    >
    > result: (text file with line length = 16, with spaces)
    > AAA BBBBBBBBBBB
    > CCCCCCCCC
    > DDDDDDDDDDDDDDD
    > EEEEE FF G
    >
    > Each line of the output file is composed of Item texts separated by
    > one space.
    > If by adding a new item text on the line we have more than 16
    > characters then pad the current line with spaces and copy the item
    > text to a new line.
    >
    > I do not know if it is possible to do that in XSLT.
    > Thanks for your help.
    >
    > Olivier
    >
    Dimitre Novatchev, Jan 31, 2007
    #3
  4. Guest

    > Dead easy, just add <xsl:eek:utput method="text" /> to the top of your
    > stylesheet.http://www.w3.org/TR/xslt#section-Text-Output-Method


    Thanks, but I do not understand your answer. How to limit the line
    length to 16 chars ? How to group small item texts in one line ? How
    to separate item text with space ?
    , Jan 31, 2007
    #4
  5. Guest

    On Jan 31, 4:02 pm, ""
    <> wrote:
    > <Source>
    > <Item>AAA</Item>
    > <Item>BBBBBBBBBBB</Item>
    > <Item>CCCCCCCCC</Item>
    > <Item>DDDDDDDDDDDDDDD</Item>
    > <Item>EEEEE</Item>
    > <Item>FF</Item>
    > <Item>G</Item>
    > </Source>
    >
    > result: (text file with line length = 16, with spaces)
    > AAA BBBBBBBBBBB
    > CCCCCCCCC
    > DDDDDDDDDDDDDDD
    > EEEEE FF G
    >
    > Each line of the output file is composed of Item texts
    > separated by one space.
    > If by adding a new item text on the line we have more
    > than 16 characters then pad the current line with spaces
    > and copy the item text to a new line.
    >
    > I do not know if it is possible to do that in XSLT.


    It certainly is possible, but fairly... perverse. XSLT is
    not very good at stuff like that.

    Just for the heck of it:

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:eek:utput method="text"/>
    <xsl:param name="max-length" select="16"/>
    <xsl:param name="pad-with" select="'.'"/>
    <xsl:template match="Source">
    <xsl:apply-templates select="Item[1]"/>
    </xsl:template>
    <xsl:template match="Item">
    <xsl:param name="cur-position" select="0"/>
    <xsl:variable name="cur-length"
    select="string-length(.)"/>
    <xsl:variable name="new-position">
    <xsl:choose>
    <xsl:when test="0 = $cur-position">
    <xsl:value-of
    select="$cur-position + $cur-length"/>
    </xsl:when>
    <xsl:eek:therwise>
    <xsl:value-of
    select="$cur-position + $cur-length + 1"/>
    </xsl:eek:therwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:if
    test=
    "
    ($max-length &lt; $cur-length) or
    (0 = $cur-length)
    ">
    <xsl:message
    terminate="yes">Invalid entity.</xsl:message>
    </xsl:if>
    <xsl:choose>
    <xsl:when test="$new-position &gt; $max-length">
    <xsl:call-template name="pad-space">
    <xsl:with-param name="position"
    select="$cur-position"/>
    </xsl:call-template>
    <xsl:text>
    </xsl:text>
    <xsl:value-of select="."/>
    <xsl:apply-templates
    select="following-sibling::Item[1]">
    <xsl:with-param name="cur-position"
    select="$cur-length"/>
    </xsl:apply-templates>
    <xsl:if test="not(following-sibling::Item[1])">
    <xsl:call-template name="pad-space">
    <xsl:with-param name="position"
    select="$cur-position"/>
    </xsl:call-template>
    <xsl:text>
    </xsl:text>
    </xsl:if>
    </xsl:when>
    <xsl:eek:therwise>
    <xsl:if test="0 &lt; $cur-position">
    <xsl:value-of select="$pad-with"/>
    </xsl:if>
    <xsl:value-of select="."/>
    <xsl:apply-templates
    select="following-sibling::Item[1]">
    <xsl:with-param name="cur-position"
    select="$new-position"/>
    </xsl:apply-templates>
    <xsl:if test="not(following-sibling::Item[1])">
    <xsl:call-template name="pad-space">
    <xsl:with-param name="position"
    select="$new-position"/>
    </xsl:call-template>
    <xsl:text>
    </xsl:text>
    </xsl:if>
    </xsl:eek:therwise>
    </xsl:choose>
    </xsl:template>
    <xsl:template name="pad-space">
    <xsl:param name="position"/>
    <xsl:variable name="left"
    select="$max-length - $position"/>
    <xsl:if test="0 &lt; $left">
    <xsl:value-of select="$pad-with"/>
    <xsl:call-template name="pad-space">
    <xsl:with-param name="position"
    select="$position + 1"/>
    </xsl:call-template>
    </xsl:if>
    </xsl:template>
    </xsl:stylesheet>

    Not tested on anything but the sample document provided by
    the OP. Note that on large documents it is likely to barf
    all over the stack and die horrible death. So I would
    strongly advise against deploying it in production
    environments without some serious tinkering beforehand
    (preferrably the tinkering would involve rewriting the
    transformation in a language more suitable for the task).

    --
    Pavel Lepin
    , Jan 31, 2007
    #5
  6. Guest

    Thank you very much for your help.
    As the input source file is also generated by an XSLT transformation,
    do you think I can hook your function inside this transformation, and
    avoid the intermediary file ?

    Olivier
    , Jan 31, 2007
    #6
  7. Guest

    Thanks Pavel!
    , Jan 31, 2007
    #7
  8. <> wrote in message
    news:...
    > Thank you very much for your help.
    > As the input source file is also generated by an XSLT transformation,
    > do you think I can hook your function inside this transformation, and
    > avoid the intermediary file ?
    >
    > Olivier
    >


    Yes, this is a standard practice.

    In XSLT 1.0 use the xxx:node-set() extension to convert the RTF results of
    the first transformation to a regular tree.

    In XSLT 2.0 this is not necessary.


    Cheers,
    Dimitre Novatchev
    Dimitre Novatchev, Feb 1, 2007
    #8
  9. > Note that on large documents it is likely to barf
    > all over the stack and die horrible death. So I would
    > strongly advise against deploying it in production



    Not necessarily. One can use a DVC (Divide and Conquer) approach, which
    requires as little as Log2(N) maximum stack depth.


    Cheers,
    Dimitre Novatchev

    <> wrote in message
    news:...
    > On Jan 31, 4:02 pm, ""
    > <> wrote:
    >> <Source>
    >> <Item>AAA</Item>
    >> <Item>BBBBBBBBBBB</Item>
    >> <Item>CCCCCCCCC</Item>
    >> <Item>DDDDDDDDDDDDDDD</Item>
    >> <Item>EEEEE</Item>
    >> <Item>FF</Item>
    >> <Item>G</Item>
    >> </Source>
    >>
    >> result: (text file with line length = 16, with spaces)
    >> AAA BBBBBBBBBBB
    >> CCCCCCCCC
    >> DDDDDDDDDDDDDDD
    >> EEEEE FF G
    >>
    >> Each line of the output file is composed of Item texts
    >> separated by one space.
    >> If by adding a new item text on the line we have more
    >> than 16 characters then pad the current line with spaces
    >> and copy the item text to a new line.
    >>
    >> I do not know if it is possible to do that in XSLT.

    >
    > It certainly is possible, but fairly... perverse. XSLT is
    > not very good at stuff like that.
    >
    > Just for the heck of it:
    >
    > <xsl:stylesheet version="1.0"
    > xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    > <xsl:eek:utput method="text"/>
    > <xsl:param name="max-length" select="16"/>
    > <xsl:param name="pad-with" select="'.'"/>
    > <xsl:template match="Source">
    > <xsl:apply-templates select="Item[1]"/>
    > </xsl:template>
    > <xsl:template match="Item">
    > <xsl:param name="cur-position" select="0"/>
    > <xsl:variable name="cur-length"
    > select="string-length(.)"/>
    > <xsl:variable name="new-position">
    > <xsl:choose>
    > <xsl:when test="0 = $cur-position">
    > <xsl:value-of
    > select="$cur-position + $cur-length"/>
    > </xsl:when>
    > <xsl:eek:therwise>
    > <xsl:value-of
    > select="$cur-position + $cur-length + 1"/>
    > </xsl:eek:therwise>
    > </xsl:choose>
    > </xsl:variable>
    > <xsl:if
    > test=
    > "
    > ($max-length &lt; $cur-length) or
    > (0 = $cur-length)
    > ">
    > <xsl:message
    > terminate="yes">Invalid entity.</xsl:message>
    > </xsl:if>
    > <xsl:choose>
    > <xsl:when test="$new-position &gt; $max-length">
    > <xsl:call-template name="pad-space">
    > <xsl:with-param name="position"
    > select="$cur-position"/>
    > </xsl:call-template>
    > <xsl:text>
    > </xsl:text>
    > <xsl:value-of select="."/>
    > <xsl:apply-templates
    > select="following-sibling::Item[1]">
    > <xsl:with-param name="cur-position"
    > select="$cur-length"/>
    > </xsl:apply-templates>
    > <xsl:if test="not(following-sibling::Item[1])">
    > <xsl:call-template name="pad-space">
    > <xsl:with-param name="position"
    > select="$cur-position"/>
    > </xsl:call-template>
    > <xsl:text>
    > </xsl:text>
    > </xsl:if>
    > </xsl:when>
    > <xsl:eek:therwise>
    > <xsl:if test="0 &lt; $cur-position">
    > <xsl:value-of select="$pad-with"/>
    > </xsl:if>
    > <xsl:value-of select="."/>
    > <xsl:apply-templates
    > select="following-sibling::Item[1]">
    > <xsl:with-param name="cur-position"
    > select="$new-position"/>
    > </xsl:apply-templates>
    > <xsl:if test="not(following-sibling::Item[1])">
    > <xsl:call-template name="pad-space">
    > <xsl:with-param name="position"
    > select="$new-position"/>
    > </xsl:call-template>
    > <xsl:text>
    > </xsl:text>
    > </xsl:if>
    > </xsl:eek:therwise>
    > </xsl:choose>
    > </xsl:template>
    > <xsl:template name="pad-space">
    > <xsl:param name="position"/>
    > <xsl:variable name="left"
    > select="$max-length - $position"/>
    > <xsl:if test="0 &lt; $left">
    > <xsl:value-of select="$pad-with"/>
    > <xsl:call-template name="pad-space">
    > <xsl:with-param name="position"
    > select="$position + 1"/>
    > </xsl:call-template>
    > </xsl:if>
    > </xsl:template>
    > </xsl:stylesheet>
    >
    > Not tested on anything but the sample document provided by
    > the OP. Note that on large documents it is likely to barf
    > all over the stack and die horrible death. So I would
    > strongly advise against deploying it in production
    > environments without some serious tinkering beforehand
    > (preferrably the tinkering would involve rewriting the
    > transformation in a language more suitable for the task).
    >
    > --
    > Pavel Lepin
    >
    Dimitre Novatchev, Feb 1, 2007
    #9
  10. Guest

    On Feb 1, 3:50 am, "Dimitre Novatchev"
    <> wrote:
    > > Note that on large documents it is likely to barf
    > > all over the stack and die horrible death. So I would
    > > strongly advise against deploying it in production

    >
    > Not necessarily.


    I was talking specifically about the implementation I
    provided.

    > One can use a DVC (Divide and Conquer) approach, which
    > requires as little as Log2(N) maximum stack depth.


    Wouldn't there be certain problems with implementing DVC
    for this task? The position of every Item depends on all
    previous Items after all. Tail recursion would seem like a
    better approach to me, but I not sure there are any XSLT
    processors to date that can optimize it away. (Are there,
    by the way?)

    --
    Pavel Lepin
    , Feb 1, 2007
    #10
  11. > Wouldn't there be certain problems with implementing DVC
    > for this task? The position of every Item depends on all
    > previous Items after all.


    No, there's usually no problem with this. Look how the DVC version of
    foldl() is implemented in FXSL:


    <xsl:stylesheet version="2.0"
    xmlns:f="http://fxsl.sf.net/"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    exclude-result-prefixes="f">

    <xsl:import href="func-apply.xsl"/>
    <xsl:import href="func-drop.xsl"/>

    <xsl:function name="f:foldl">
    <xsl:param name="pFunc" as="element()"/>
    <xsl:param name="pA0"/>
    <xsl:param name="pList" as="item()*"/>

    <xsl:sequence select=
    "if(empty($pList))
    then
    $pA0
    else
    for $vcntList in count($pList) return
    if($vcntList = 1)
    then
    f:apply($pFunc, $pA0, $pList[1])
    else
    for $vHalfLen in ($vcntList idiv 2) return
    f:foldl($pFunc,
    f:foldl($pFunc, $pA0, $pList[position() le
    $vHalfLen]),
    f:drop($vHalfLen, $pList)
    )"/>

    </xsl:function>

    </xsl:stylesheet>


    Something similar is done in FXSL 1.x (For XSLT 1.0).

    > Tail recursion would seem like a
    > better approach to me, but I not sure there are any XSLT
    > processors to date that can optimize it away.


    Some do (Net XslCompiledTransform, Saxon6.5, JD are all very good at
    optimizing TR), some don't, and all 3 XSLT 2.0 processors have some
    difficulties -- see more on this here:

    http://dnovatchev.spaces.live.com/Blog/cns!44B0A32C2CCF7488!345.entry

    Cheers,
    Dimitre Novatchev



    <> wrote in message
    news:...
    > On Feb 1, 3:50 am, "Dimitre Novatchev"
    > <> wrote:
    >> > Note that on large documents it is likely to barf
    >> > all over the stack and die horrible death. So I would
    >> > strongly advise against deploying it in production

    >>
    >> Not necessarily.

    >
    > I was talking specifically about the implementation I
    > provided.
    >
    >> One can use a DVC (Divide and Conquer) approach, which
    >> requires as little as Log2(N) maximum stack depth.

    >
    > Wouldn't there be certain problems with implementing DVC
    > for this task? The position of every Item depends on all
    > previous Items after all. Tail recursion would seem like a
    > better approach to me, but I not sure there are any XSLT
    > processors to date that can optimize it away. (Are there,
    > by the way?)
    >
    > --
    > Pavel Lepin
    >
    Dimitre Novatchev, Feb 1, 2007
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. rodrigo guerra

    automatic code formating...

    rodrigo guerra, Jul 5, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    348
    rodrigo guerra
    Jul 5, 2003
  2. Dmitri Manushin

    Formating text

    Dmitri Manushin, Aug 14, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    375
    Dmitri Manushin
    Aug 14, 2003
  3. Dips

    formating of text file

    Dips, May 19, 2005, in forum: Java
    Replies:
    2
    Views:
    382
    Matt Humphrey
    May 19, 2005
  4. comp_novice

    file formating...help needed

    comp_novice, Feb 4, 2005, in forum: C Programming
    Replies:
    3
    Views:
    279
    Martin Ambuhl
    Feb 4, 2005
  5. J
    Replies:
    8
    Views:
    215
Loading...

Share This Page