Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

count the nodes ignoring blank nodes

Tags:

xslt

xslt-2.0

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

like image 592
user3872094 Avatar asked Jun 09 '15 10:06

user3872094


1 Answers

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.

like image 101
Sean B. Durkin Avatar answered Oct 19 '22 05:10

Sean B. Durkin