xslt-1.0

XSLT retrieve number of lines in texts


I'm completely new to XSLT and I've the following problem to solve, hoping in some suggestions and help. With this xml as input

<?xml version="1.0" encoding="utf-8"?>
<WorkingCycle xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Operations>
    <WorkingCycleOperation>
      <OperationID>PICKING</OperationID>
    </WorkingCycleOperation>
    <WorkingCycleOperation>
      <OperationID>CUTTING</OperationID>
      <OperationNote>Lorem ipsum dolor sit amet, consectetur adipiscing elit</OperationNote>
    </WorkingCycleOperation>
    <WorkingCycleOperation>
      <OperationID>FEEDING</OperationID>
      <OperationNote>sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</OperationNote>
    </WorkingCycleOperation>
    <WorkingCycleOperation>
      <OperationID>MELTING</OperationID>
    </WorkingCycleOperation>
    <WorkingCycleOperation>
      <OperationID>BRUSHING</OperationID>
      <OperationNote>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat</OperationNote>
    </WorkingCycleOperation>enter image description here
    <WorkingCycleOperation>
      <OperationID>PACKING</OperationID>
    </WorkingCycleOperation>
  </Operations>
</WorkingCycle>

I need to determine, for each operation that has a note (<OperationNote> tag), how many rows the note is composed by, assuming to split the long text of the note into short pieces of 20 characters each. That is to say, giving the first text string length of 55 characters, it will be splitted into 3 substrings of 20+20+15 char's, returning a total of three lines. The second text string has a total of 65 char's, it will split into 4 substrings of 20+20+20+5 chars, returning 4 lines. For the third, 106 char's in 20+20+20+20+20+6 substrings, 6 lines. In the result, I'll need to list the operation ID and the lines from/to where that operation will have the text. That is to say, first operation with a note will have texts in lines from 1 to 3, second from lines 4 to 7, the third from lines 8 to 13.

I do hope I've been clear enough... Any help and suggestion is very much appreciate. Thanks a lot

The result should be something like this:

    <?xml version='1.0' ?>
    <Line>
      <OperationID>CUTTING</OperationID>
      <FromLine>1</FromLine>
      <ToLine>3</ToLine>
    </Line>
    <Line>
    <OperationID>FEEDING</OperationID>
      <FromLine>4</FromLine>
      <ToLine>7</ToLine>
    </Line>
    <Line>
      <OperationID>BRUSHING</OperationID>
      <FromLine>8</FromLine>
      <ToLine>13</ToLine>
   </Line>

Here below, I'm adding what I did so far, however not working as expected:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="http://www.sap.com/sapxsl" version="1.0">
  <xsl:template match="WorkingCycle">
    <processResponse>
      <xsl:for-each select="Operations">
        <xsl:for-each select="WorkingCycleOperation">
          <xsl:variable name="OperID" select="OperationID"/>
          <!--          <xsl:for-each select="OperationNote">-->
          <xsl:if test="OperationNote">

            <xsl:variable name="j" select="position()"/>
            <xsl:variable name="opNote" select="OperationNote"/>
            <xsl:variable name="lines" select="ceiling(string-length($opNote) div 20)"/>
            <xsl:variable name="end" select="$lines"/>
            <xsl:choose>
              <xsl:when test="$j &gt; 1">
                <xsl:variable name="start" select="$end + 1"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:variable name="start" select="1"/>
                <!--                <xsl:variable name="end" select="$lines"/>-->
              </xsl:otherwise>
            </xsl:choose>

            <OperationID>
              <xsl:value-of select="$OperID"/>
            </OperationID>
            <FromLine>
              <xsl:value-of select="$start"/>
            </FromLine>
            <ToLine>
              <xsl:value-of select="$end"/>
            </ToLine>
          </xsl:if>
          <!--          </xsl:for-each>-->
        </xsl:for-each>
      </xsl:for-each>
    </processResponse>
  </xsl:template>
</xsl:transform>

Result so far looks like this:

enter image description here

Thank you!


Solution

  • As I told you earlier, producing a running total in XSLT is not a trivial task, esp. in XSLT 1.0. It cannot be done within xsl:for-each because xsl:for-each is not a loop. You need to use some other method, for example a technique known as "sibling recursion":

    XSLT 1.0

    <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="/WorkingCycle">
        <processResponse>
            <!-- start with the first node -->
            <xsl:apply-templates select="Operations/WorkingCycleOperation[OperationNote][1]"/>
        </processResponse>
    </xsl:template>
    
    <xsl:template match="WorkingCycleOperation">
        <xsl:param name="count" select="0"/>
        <xsl:variable name="next" select="$count + ceiling(string-length(OperationNote) div 20)" />
        <!-- output -->
        <Line>
            <OperationID>
                <xsl:value-of select="OperationID"/>
            </OperationID>
            <FromLine>
                <xsl:value-of select="$count + 1"/>
            </FromLine>
            <ToLine>
                <xsl:value-of select="$next"/>
            </ToLine>
        </Line> 
        <!-- recursive call --> 
        <xsl:apply-templates select="following-sibling::WorkingCycleOperation[OperationNote][1]">
            <xsl:with-param name="count" select="$next"/>
        </xsl:apply-templates>
    </xsl:template>
        
    </xsl:stylesheet>