Re: [xsl] range creation

Subject: Re: [xsl] range creation
From: "G. Ken Holman" <gkholman@xxxxxxxxxxxxxxxxxxxx>
Date: Sat, 19 Nov 2011 08:50:31 -0500
At 2011-11-19 14:55 +0530, Joga Singh Rawat wrote:
Hi guys,
My input is <link href="#bib40 #bib41 #bib42 #bib45 #bib40 #bib41 #bib45"/>
and <link href="#bib40"/><link href="#bib41"/><link href="#bib42"/><link
href="#bib45"/><link href="#bib40"/><link href="#bib41"/><link
href="#bib45"/> and I want create a range of numbers, if any three numbers
are in sequence as displayed in "Required output".

Any clue is appreciated.

Required Output
<a href="#bib40">1</a><a href="#bib41">2</a><a href="#bib42">3</a><a
href="#bib45">6</a><a href="#bib40">1</a><a href="#bib41">2</a><a
href="#bib45">6</a>.
Should be
<a href="#bib40">1</a>--<a href="#bib42">3</a>, <a href="#bib45">6</a>, <a
href="#bib40"> 1</a>, <a href="#bib41"> 2</a>, <a href="#bib45"> 6</a>.

Since you are using XSLT 2.0, group-starting-with= is your friend!


Below is a heavily-commented solution. In fact the number of instructions when using <xsl:for-each-group> is quite small compared to the code you posted because I'm letting the processor do the heavy lifting. I focused only on the link grouping logic and I'll leave it with you to decorate your links as you want.

You didn't say what to do with a hybrid collection of links, such as:

   <p>Range test: <link href="#bib40 #bib41"/><link
href="#bib42"/><link href="#bib45"/><link href="#bib40"/><link
href="#bib41"/><link href="#bib45"/>.

... so I assumed this should also be: 1-3, 6, 1, 2, 6.

The solution involves first finding all of the link elements in a sequence, then distilling all of the individual links into a new sequence, acting on groups found in that new sequence, and then repositioning back into the source node tree for each member of the sequence being acted on.

I'm also assuming the range determination is based on the sequence number of the link in the bibliography. If this isn't the case, I've commented where in the distillation you calculate that on which you base your range sequencing.

I think this approach is quite robust ... I hope it helps.

. . . . . . . . . . . Ken

~/t/ftemp $ cat joga.xml
<?xml version="1.0" encoding="UTF-8"?>
<component xmlns="http://www.wiley.com/namespaces/wiley";>
   <p>Range test: <link href="#bib40 #bib41 #bib42 #bib45 #bib40 #bib41
#bib45"/>. This should be 1-3, 6, 1, 2, 6.</p>
   <p>Range Test: <link href="#bib40"/><link href="#bib41"/><link
href="#bib42"/><link href="#bib45"/><link href="#bib40"/><link
href="#bib41"/><link href="#bib45"/>. This should be 1-3, 6, 1, 2, 6.</p>
   <p>Individual test: <link href="#bib40"/>, <link href="#bib41"/>, <link
href="#bib45"/>.</p>
   <p>Range test: <link href="#bib40 #bib41"/><link
href="#bib42"/><link href="#bib45"/><link href="#bib40"/><link
href="#bib41"/><link href="#bib45"/>. Should this be 1-3, 6, 1, 2, 6?</p>
   <bibliography style="numbered">
    <bib xml:id="bib40">bib1</bib>
    <bib xml:id="bib41">bib2</bib>
    <bib xml:id="bib42">bib3</bib>
    <bib xml:id="bib43">bib4</bib>
    <bib xml:id="bib44">bib5</bib>
    <bib xml:id="bib45">bib6</bib>
   </bibliography>
</component>
~/t/ftemp $
~/t/ftemp $ xslt2 joga.xml joga.xsl
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <title>Link test</title>
   </head>
   <body>

<p>Range test: <a href="#bib40">1</a>-<a href="#bib42">3</a>, <a href="#bib45">6</a>, <a href="#bib40">1</a>, <a href="#bib41">2</a>, <a href="#bib45">6</a>. This should be 1-3, 6, 1, 2, 6.
</p>


<p>Range Test: <a href="#bib40">1</a>-<a href="#bib42">3</a>, <a href="#bib45">6</a>, <a href="#bib40">1</a>, <a href="#bib41">2</a>, <a href="#bib45">6</a>. This should be 1-3, 6, 1, 2, 6.
</p>


<p>Individual test: <a href="#bib40">1</a>, <a href="#bib41">2</a>, <a href="#bib45">6</a>.
</p>


<p>Range test: <a href="#bib40">1</a>-<a href="#bib42">3</a>, <a href="#bib45">6</a>, <a href="#bib40">1</a>, <a href="#bib41">2</a>, <a href="#bib45">6</a>. Should this be 1-3, 6, 1, 2, 6?
</p>


      <ol>
         <p name="bib40">bib1</p>
         <p name="bib41">bib2</p>
         <p name="bib42">bib3</p>
         <p name="bib43">bib4</p>
         <p name="bib44">bib5</p>
         <p name="bib45">bib6</p>
      </ol>

   </body>
</html>~/t/ftemp $
~/t/ftemp $ cat joga.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
                xmlns:w="http://www.wiley.com/namespaces/wiley";
                exclude-result-prefixes="w"
                version="2.0">

<xsl:template match="w:component">
  <html>
    <head><title>Link test</title></head>
    <body><xsl:apply-templates/></body>
  </html>
</xsl:template>

<xsl:variable name="doc" select="/"/>

<xsl:template match="w:p">
<p>
<!--act on sequences of links-->
<!--by grouping starting with those that are not links, those in a sequence
of links end up all in one group-->
<xsl:for-each-group select="node()"
group-starting-with="node()[not(self::w:link)]">
<!--process everything in the group that isn't a link-->
<xsl:apply-templates select="current-group()[not(self::w:link)]"/>
<!--process everything in the group that is a link (all are at end)-->
<!--next present the ranged list of bibliographic references-->
<!--distill all link information in specified order of singletons-->
<xsl:variable name="links">
<xsl:for-each
select="current-group()[self::w:link]/@href/tokenize(.,'(\s+|#)')[.]">
<xsl:for-each select="id(.,$doc)">
<distilled-link idref="{@xml:id}">
<!--the bibliographic sequence number -->
<xsl:value-of select="count(preceding-sibling::*
[node-name(.)=node-name(current())]) + 1"/>
</distilled-link>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<!--group all singleton links based on sequences of two or more-->
<!--by grouping staring with those not in a sequence, those in a sequence
end up all in one group-->
<xsl:for-each-group select="$links/distilled-link"
group-starting-with="distilled-link
[. != (preceding-sibling::*[1]+1)]">
<xsl:choose>
<xsl:when test="count(current-group())>2">
<!--found a range of sequential links-->
<xsl:apply-templates select="."/>
<xsl:text>-</xsl:text>
<xsl:apply-templates select="current-group()[last()]"/>
</xsl:when>
<xsl:otherwise>
<!--found a standalone or a pair of links-->
<xsl:for-each select="current-group()">
<xsl:if test="position()>1">, </xsl:if>
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
<!--add a delimiter; assume terminator supplied in the source-->
<xsl:if test="position()!=last()">, </xsl:if>
</xsl:for-each-group>
</xsl:for-each-group>
</p>
</xsl:template>


<xsl:template match="distilled-link">
  <xsl:for-each select="id(@idref,$doc)">
    <!--now positioned at the bibliographic reference ready for processing-->
    <a href="#{@xml:id}"><xsl:number/></a>
  </xsl:for-each>
</xsl:template>

<xsl:template match="w:bibliography">
  <ol>
    <xsl:for-each select="w:bib">
      <p name="{@xml:id}"><xsl:apply-templates/></p>
    </xsl:for-each>
  </ol>
</xsl:template>

</xsl:stylesheet>~/t/ftemp $
~/t/ftemp $


-- Contact us for world-wide XML consulting and instructor-led training Free 5-hour video lecture: XSLT/XPath 1.0 & 2.0 http://ude.my/t37DVX Crane Softwrights Ltd. http://www.CraneSoftwrights.com/s/ G. Ken Holman mailto:gkholman@xxxxxxxxxxxxxxxxxxxx Google+ profile: https://plus.google.com/116832879756988317389/about Legal business disclaimers: http://www.CraneSoftwrights.com/legal

Current Thread