6

I've the below XML.

Case 1

<body>
    <nd>
        <pnn>1.1</pnn>
        <h1>PART 54</h1>
        <ti>Construction</ti>
    </nd>
    <nd>
        <h1>PART 54</h1>
        <h2>I INTRODUCT</h2>
        <ti>Time</ti>
    </nd>
    <nd>
        <h1>PART 54</h1>
        <h2>I INTRODUCT</h2>
        <ti>Power</ti>
    </nd>
    <nd>
        <h1>PART 54</h1>
        <h2>II APPLICATIONS</h2>
        <ti>Filing</ti>
    </nd>
</body>

Case 2

<body>
    <nd>
        <pnn>1.1</pnn>
        <h1>PART 54</h1>
        <h2>I INTRODUCT</h2>
        <ti>Construction</ti>
    </nd>
    <nd>
        <h1>PART 54</h1>
        <h2>I INTRODUCT</h2>
        <ti>Time</ti>
    </nd>
    <nd>
        <h1>PART 54</h1>
        <h2>II APPLICATIONS</h2>
        <ti>Filing</ti>
    </nd>
</body>

and the below XSLT

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

    <xsl:template match="/">
      <hmtl>
        <head>
          <title>New Version!</title>
        </head>
       <xsl:apply-templates select="body"></xsl:apply-templates>
      </hmtl>
    </xsl:template>

    <xsl:template match="body">
         <xsl:for-each select="nd">
            <xsl:apply-templates select = "."/>
        </xsl:for-each>
    </xsl:template>
   <xsl:template match="pnn"/>
   <xsl:template match="h1"/>
   <xsl:template match="h2"/>
   <xsl:template match="ti"/>
<xsl:variable name="FinalChap">
        <xsl:value-of select="substring-before((//pnn)[1],'.')"/>
    </xsl:variable>
    <xsl:variable name="FinalChn">
        <xsl:value-of select="$FinalChap"/>
    </xsl:variable>
    <xsl:variable name="Finalchapternumber">
        <xsl:value-of select="format-number($FinalChn,'00')"/>
    </xsl:variable>

    <xsl:template name="section" match="nd">
        <xsl:variable name="count">
            <xsl:number count="nd" level="any"/>
        </xsl:variable>
        <xsl:variable name="classname">
            <!--Get name attribute of current node -->
            <xsl:value-of select="concat('section-sect','1')"/>
        </xsl:variable>
        <xsl:variable name="classname1">
            <!--Get name attribute of current node -->
            <xsl:value-of select="concat('section-sect','2')"/>
        </xsl:variable>
        <xsl:variable name="classname2">
            <!--Get name attribute of current node -->
            <xsl:value-of select="concat('section-sect','3')"/>
        </xsl:variable>

        <!--Create a string variable by concat string method  -->
        <xsl:variable name="sectionname">
            <xsl:value-of select="concat('CH_',$Finalchapternumber,'-SEC-', $count)"/>
        </xsl:variable>
            <div class="{$classname}">
            <xsl:if test="./h2 and not(preceding::h2[1]/text() = ./h2/text())">
                <a name="{$sectionname}"> </a>
                <div class="section-title">
                    <xsl:if test="not(preceding::h2[1]/text() = ./h2/text())">
                        <xsl:apply-templates select="h2" mode="section"/>
                    </xsl:if>
                </div>
            </xsl:if>
            <xsl:if test="not(lower-case(./ti/text()) = lower-case(./h2/text()))">
                <xsl:if test="./ti">
                    <div class="{$classname2}">
                        <xsl:apply-templates select="ti" mode="section"/>
                    </div>
                </xsl:if>
            </xsl:if>
            <xsl:apply-templates select="child::node()[not(self::h2|self::ti)]"/>
        </div>
    </xsl:template>
    <xsl:template match="ti" mode="section">
        <xsl:apply-templates select="./node()[1][self::page]" mode="first"/>
        <xsl:variable name="sectionnum">
            <xsl:number count="nd" level="any"/>
        </xsl:variable>
        <a name="CH_{$Finalchapternumber}-SEC-{$sectionnum}"/>
        <div class="section-title">
            <xsl:apply-templates/>
        </div>
    </xsl:template>
    <xsl:template match="h2" mode="section">
        <div class="section-title">
            <xsl:apply-templates select="child::node()[not(self::fnt)]"/>
        </div>
    </xsl:template>


</xsl:transform>

here I'm trying to increment the section number based on a condition. The count should be done, if there is no node(here h2) <a name="CH_01-SEC-XX"></a> should be ignored I'm able to do it using <xsl:if test="./h2 and not(preceding::h2[1]/text() = ./h2/text())">, but the challenge i'm facing is count is not ignoring it.

Current output. Case 1

<div class="section-sect1">
    <a name="CH_01-SEC-1"/>
    <div class="section-title">
        <div class="section-title">I INTRODUCT</div>
    </div>
    <div class="section-sect3">
        <a name="CH_01-SEC-1"/>
        <div class="section-title">Construction</div>
    </div>
</div>
<div class="section-sect1">
    <div class="section-sect3">
        <a name="CH_01-SEC-2"/>
        <div class="section-title">Time</div>
    </div>
</div>
<div class="section-sect1">
    <div class="section-sect3">
        <a name="CH_01-SEC-3"/>
        <div class="section-title">Power</div>
    </div>
</div>
<div class="section-sect1">
    <a name="CH_01-SEC-4"/>
    <div class="section-title">
        <div class="section-title">II APPLICATIONS</div>
    </div>
    <div class="section-sect3">
        <a name="CH_01-SEC-4"/>
        <div class="section-title">Filing</div>
    </div>
</div>

Expected output Case 1

<div class="section-sect1">
    <a name="CH_01-SEC-1"/>
    <div class="section-title">
        <div class="section-title">I INTRODUCT</div>
    </div>
    <div class="section-sect3">
        <a name="CH_01-SEC-2"/>
        <div class="section-title">Construction</div>
    </div>
</div>
<div class="section-sect1">
    <div class="section-sect3">
        <a name="CH_01-SEC-3"/>
        <div class="section-title">Time</div>
    </div>
</div>
<div class="section-sect1">
    <div class="section-sect3">
        <a name="CH_01-SEC-4"/>
        <div class="section-title">Power</div>
    </div>
</div>
<div class="section-sect1">
    <a name="CH_01-SEC-5"/>
    <div class="section-title">
        <div class="section-title">II APPLICATIONS</div>
    </div>
    <div class="section-sect3">
        <a name="CH_01-SEC-6"/>
        <div class="section-title">Filing</div>
    </div>
</div>

Current Output Case 2

<div class="section-sect1"><a name="CH_01-SEC-1"></a><div class="section-title">
         <div class="section-title">I INTRODUCT</div>
      </div>
      <div class="section-sect3"><a name="CH_01-SEC-1"></a><div class="section-title">Construction</div>
      </div>
      </div>
   <div class="section-sect1">
      <div class="section-sect3"><a name="CH_01-SEC-2"></a><div class="section-title">Time</div>
      </div>
   </div>
   <div class="section-sect1"><a name="CH_01-SEC-3"></a><div class="section-title">
         <div class="section-title">II APPLICATIONS</div>
      </div>
      <div class="section-sect3"><a name="CH_01-SEC-3"></a><div class="section-title">Filing</div>
      </div>
   </div>

Expected output Case 2

<div class="section-sect1">
    <a name="CH_01-SEC-1"/>
    <div class="section-title">
        <div class="section-title">I INTRODUCT</div>
    </div>
    <div class="section-sect3">
        <a name="CH_01-SEC-2"/>
        <div class="section-title">Construction</div>
    </div>
</div>
<div class="section-sect1">
    <div class="section-sect3">
        <a name="CH_01-SEC-3"/>
        <div class="section-title">Time</div>
    </div>
</div>
<div class="section-sect1">
    <a name="CH_01-SEC-4"/>
    <div class="section-title">
        <div class="section-title">II APPLICATIONS</div>
    </div>
    <div class="section-sect3">
        <a name="CH_01-SEC-5"/>
        <div class="section-title">Filing</div>
    </div>
</div>

in current output there is duplicate CH_01-SEC-1 Can someone please let me know how to make it into a series of 1...n

Here is a working DEmo

Thanks

Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65
user3872094
  • 3,269
  • 8
  • 33
  • 71
  • I don't understand your issue. Why do you count the `nd, ti, h2`? If you change both `` to `` it works fine. Is your sample XML significant enough? There's no `` in the input, it is only present in output. – potame Jun 09 '15 at 12:43
  • @potame, that is generated from ` ` – user3872094 Jun 09 '15 at 12:47
  • Yes I've seen that, but it does not answer my questions :) – potame Jun 09 '15 at 12:48
  • Hi @potame, I've updated my question with a different case, with same XSLT and changed count to count `nd` only, but here there are duplicates found instead of series. Also updated the demo link – user3872094 Jun 09 '15 at 13:15
  • I am afraid your question is still not clear. I suggest you minimize the example to only what's necessary to demonstrate the problem. – michael.hor257k Jun 11 '15 at 20:38
  • Hi @michael.hor257k, the question is `count ti | h2` and the condition is, if `./h2/text() = preceding::h2[1]/text()`, `h2` should be ignored, and also if `./h2/text()=./ti/text()`, `ti` should be ignored, the count should be in sequence. – user3872094 Jun 12 '15 at 11:07
  • A minor stylistic point: don't write `` when you could write ``. It's not just a matter of brevity: if you want your variable to be a string, it's horribly wasteful to create a tree with a document node and a text node, just so the document can be converted to a string whenever it is used. – Michael Kay Jun 17 '15 at 20:43

1 Answers1

1

How about ....

<xsl:transform
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  version="2.0">
<xsl:output method="html" doctype-public="XSLT-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />

<xsl:variable name="FinalChap">
  <xsl:value-of select="format-number( xs:integer( substring-before((/body/nd/pnn)[1],'.')), '00')"/>
</xsl:variable>

<xsl:template match="/">
  <html>
  <head><title>New Version from Sean!</title></head>
  <xsl:apply-templates />
 </html>
</xsl:template>

<xsl:template match="*">
 <xsl:apply-templates />
</xsl:template>

<xsl:template match="text()|processing-instruction()|comment()|@*" />

<xsl:template match="body">
  <xsl:for-each-group select="nd" group-adjacent="h2/text()">
    <xsl:variable name="group-position" select="position()" />
    <xsl:for-each select="current-group()">
     <xsl:call-template name="nd">
      <xsl:with-param name="group-position" select="$group-position" />
      <xsl:with-param name="is-head" select="position() eq 1" as="xs:boolean" />
     </xsl:call-template>  
    </xsl:for-each>  
  </xsl:for-each-group>
</xsl:template>

<xsl:template name="nd">
 <xsl:param name="group-position" select="1" as="xs:integer" />
 <xsl:param name="is-head" select="true()" as="xs:boolean" />

 <div class="section-sect1">
  <xsl:if test="$is-head"> 
   <xsl:call-template name="a-link">
    <xsl:with-param name="group-position" select="$group-position" />
    <xsl:with-param name="delta" select="0" as="xs:integer" />
   </xsl:call-template>  
   <div class="section-title">
    <div class="section-title"><xsl:value-of select="h2" /></div>
   </div>
   </xsl:if>  
  <div class="section-sect3">
    <xsl:call-template name="a-link">
     <xsl:with-param name="group-position" select="$group-position" />
     <xsl:with-param name="delta" select="1" as="xs:integer" />
    </xsl:call-template>  
    <div class="section-title"><xsl:value-of select="ti" /></div>
  </div>
 </div>
</xsl:template>


<xsl:template name="a-link">
 <xsl:param name="group-position" select="1" as="xs:integer" />
 <xsl:param name="delta" select="0" as="xs:integer" />
  <a name="CH_{$FinalChap}-SEC-{$group-position + count(preceding-sibling::nd) + $delta}" /> 
</xsl:template>  

</xsl:transform>

The above transform, when applied to input document ...

<body>
    <nd>
        <pnn>1.1</pnn>
        <h1>PART 54</h1>
        <h2>I INTRODUCT</h2>
        <ti>Construction</ti>
    </nd>
    <nd>
        <h1>PART 54</h1>
        <h2>I INTRODUCT</h2>
        <ti>Time</ti>
    </nd>
    <nd>
        <h1>PART 54</h1>
        <h2>I INTRODUCT</h2>
        <ti>Power</ti>
    </nd>
    <nd>
        <h1>PART 54</h1>
        <h2>II APPLICATIONS</h2>
        <ti>Filing</ti>
    </nd>
</body>

... yields output document ...

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>New Version from Sean!</title>
</head>
<div class="section-sect1">
    <a name="CH_01-SEC-1"/>
    <div class="section-title">
        <div class="section-title">I INTRODUCT</div>
    </div>
    <div class="section-sect3">
        <a name="CH_01-SEC-2"/>
        <div class="section-title">Construction</div>
    </div>
</div>
<div class="section-sect1">
    <div class="section-sect3">
        <a name="CH_01-SEC-3"/>
        <div class="section-title">Time</div>
    </div>
</div>
<div class="section-sect1">
    <div class="section-sect3">
        <a name="CH_01-SEC-4"/>
        <div class="section-title">Power</div>
    </div>
</div>
<div class="section-sect1">
    <a name="CH_01-SEC-5"/>
    <div class="section-title">
        <div class="section-title">II APPLICATIONS</div>
    </div>
    <div class="section-sect3">
        <a name="CH_01-SEC-6"/>
        <div class="section-title">Filing</div>
    </div>
</div>
</html>

Explanation

The body template uses the xsl:for-each-group to group the nd elements by common adjacent h2 (h2 headings). The xsl:for-each-group sequence constructor invokes the nd template to process each nd element in order, passing to it the group number, and whether or not this nd is the first (the 'head') nd of the group.

I have inferred from your sample output, that the head nd of each group produces extra content being about the group, including an extra a-link.

The number of the a-link (for example 4 in CH_01-SEC-4) is equal to the count of preceding nd's, plus the group number, plus an extra 1 if we are not a head nd.

Alternative solutions

Just as there are many ways to skin a cat, there a quiet a few alternative solutions which would be equally valid. Instead of grouping, you could use a full push design. The extra content for head nodes (<div class="section-title">) could be achieved using a predicate on the template pattern, comparing this h2 with the previous h2. And the correcting number of the a-links could be achieved by micro-pipelining.

Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65
  • Hi Friend, Thanks for the solution, This worked great, but when trying to run the same with another XML, This is throwing some error, in second XML, there are `h2` randomly, i.e. not present in all `nd`, I've updated my question, can you please help me with this, here i removed `h2` in first `nd` block. Sorry not for providing in starting itself – user3872094 Jun 12 '15 at 11:01
  • @user3872094: Could you update the question to provide both test cases? A test case is a sample input document together with its expected output document. – Sean B. Durkin Jun 14 '15 at 10:52
  • Hi Friend, Updated my question with both the cases. aolng with current and expected output – user3872094 Jun 16 '15 at 07:52
  • @user3872094: I ran both test cases with the given solution, and both produced 100% correct output. I used http://fiddle.frameless.io/ for testing. There is no error. I suggest that you re-test. You may have made a typographical error and not noticed. Or perhaps there is a third test case you need to show? The supplied two test cases both work. – Sean B. Durkin Jun 17 '15 at 02:58