Reputation: 1278
Please suggest for how to select the range's between IDs. Example if range is 5-8, then 6,7 are required ids. If figs <link href="fig3">-<link href="fig7">, then fig4 fig5 fig6 are required IDs.
XML:
<root>
<p id="p1">This <link href="#fig-0001 #fig-0002"/>, <link href="#fig-0003"/>-<link href="#fig-0006"/></p>
<figure xml_id="fig-0001"><label>Fig. 1</label><caption><p>One</p></caption></figure>
<figure xml_id="fig-0002"><label>Fig. 2</label><caption><p>Two</p></caption></figure>
<figure xml_id="fig-0003"><label>Fig. 3</label><caption><p>Three</p></caption></figure>
<figure xml_id="fig-0004"><label>Fig. 4</label><caption><p>Four</p></caption></figure>
<figure xml_id="fig-0005"><label>Fig. 5</label><caption><p>Five</p></caption></figure>
<figure xml_id="fig-0006"><label>Fig. 6</label><caption><p>Six</p></caption></figure>
</root>
XSLT2:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
</xsl:template>
<xsl:key name="kFloat" match="*" use="@xml_id"/>
<xsl:template match="link[ancestor::p/following-sibling::*[1][matches(name(), '^(figure)$')]][matches(key('kFloat', if(contains(@href, ' ')) then substring-after(substring-before(@href, ' '), '#') else substring-after(@href, '#'))/name(), '^(figure)$')]">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy><!-- link element retaining-->
<!--Range between IDs selection -->
<xsl:if test="matches(preceding-sibling::node()[1][self::text()], '^(&#x2013;|&#x02013;|–|-)$')">
<xsl:variable name="varRangeFirst" select="substring-after(preceding-sibling::node()[2][name()='link']/@href, '#')"/>
<xsl:variable name="varRangeLast" select="substring-after(@href, '#')"/>
<xsl:variable name="varRangeBetweenIDs1">
<!--xsl:value-of select="for $i in key('kFloat', $varRangeLast)/preceding-sibling::figure return $i/@xml_id"/--><!-- this will select all preceding figures, but it should between 3 and 6 -->
<xsl:value-of select="for $i in key('kFloat', $varRangeLast)/preceding-sibling::figure[for $k in preceding-sibling::figure return contains($k/@xml_id, $varRangeFirst)] return $i/@xml_id"/><!-- here getting error--><!-- please suggest to select range's between IDs from this -->
<!--xsl:if test="matches(key('kFloat', $varRangeLast)/name(), '^(figure)$')">
<xsl:for-each select="key('kFloat', $varRangeLast)/preceding-sibling::figure">
<a><xsl:value-of select="@xml_id"/></a>
</xsl:for-each>
</xsl:if-->
</xsl:variable>
<xsl:for-each select="$varRangeBetweenIDs1/a">
<xsl:variable name="var2"><xsl:value-of select="preceding-sibling::a"/></xsl:variable>
<xsl:if test="contains($var2, $varRangeFirst)">
<xsl:element name="float"><xsl:attribute name="id" select="."/></xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:if>
<xsl:for-each select="tokenize(@href, ' ')"><!--for each link's individual hrefs will have respective float element -->
<float id="{substring-after(., '#')}"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Required Result:
<root>
<p id="p1">This <link href="#fig-0001 #fig-0002"/><float id="fig-0001"/><float id="fig-0002"/>, <link href="#fig-0003"/><float id="fig-0003"/>-<link href="#fig-0006"/><float id="fig-0004"/><float id="fig-0005"/><float id="fig-0006"/></p>
<figure xml_id="fig-0001"><label>Fig. 1</label><caption><p>One</p></caption></figure>
<figure xml_id="fig-0002"><label>Fig. 2</label><caption><p>Two</p></caption></figure>
<figure xml_id="fig-0003"><label>Fig. 3</label><caption><p>Three</p></caption></figure>
<figure xml_id="fig-0004"><label>Fig. 4</label><caption><p>Four</p></caption></figure>
<figure xml_id="fig-0005"><label>Fig. 5</label><caption><p>Five</p></caption></figure>
<figure xml_id="fig-0006"><label>Fig. 6</label><caption><p>Six</p></caption></figure>
</root>
Upvotes: 0
Views: 84
Reputation: 1278
With the help from Tim Sir's suggestion, I modified my answer as below.
<xsl:value-of select="for $i in key('kFloat', $varRangeLast)/preceding-sibling::figure
[some $k in preceding-sibling::figure satisfies
contains($k/@xml_id, $varRangeFirst)] return $i/@xml_id"/>
XSLT2:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
</xsl:template>
<xsl:key name="kFloat" match="*" use="@xml_id"/>
<xsl:template match="link[ancestor::p/following-sibling::*[1][matches(name(), '^(figure)$')]][matches(key('kFloat', if(contains(@href, ' ')) then substring-after(substring-before(@href, ' '), '#') else substring-after(@href, '#'))/name(), '^(figure)$')]">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy><!-- link element retaining-->
<!--Range between IDs selection -->
<xsl:if test="matches(preceding-sibling::node()[1][self::text()], '^(&#x2013;|&#x02013;|–|-)$')">
<xsl:variable name="varRangeFirst" select="substring-after(preceding-sibling::node()[2][name()='link']/@href, '#')"/>
<xsl:variable name="varRangeLast" select="substring-after(@href, '#')"/>
<xsl:variable name="varRangeBetweenIDs1">
<xsl:value-of select="for $i in key('kFloat', $varRangeLast)/preceding-sibling::figure[some $k in preceding-sibling::figure satisfies contains($k/@xml_id, $varRangeFirst)] return $i/@xml_id"/>
</xsl:variable>
<xsl:for-each select="tokenize($varRangeBetweenIDs1, ' ')">
<xsl:element name="float"><xsl:attribute name="id" select="."/></xsl:element>
</xsl:for-each>
</xsl:if>
<xsl:for-each select="tokenize(@href, ' ')"><!--for each link's individual hrefs will have respective float element -->
<float id="{substring-after(., '#')}"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 70638
I couldn't quite work out your logic from your current XSLT, so I would consider a different approach, using templates to match the various types of link
element you required. Specifically, have separate ones for link
elements that precede or follow a text node with a hyphen in.
Try this XSLT. This makes use of the intersect
function to get the range of elements you require in the case of 003-006.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kFloat" match="figure" use="@xml_id"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>
</xsl:template>
<xsl:template match="p/link[following-sibling::node()[1][self::text()][. = '-']][following-sibling::node()[2][self::link]]" priority="3">
<xsl:call-template name="identity" />
<xsl:apply-templates select="key('kFloat', substring-after(@href, '#'))" mode="float" />
</xsl:template>
<xsl:template match="p/link[preceding-sibling::node()[1][self::text()][. = '-']][preceding-sibling::node()[2][self::link]]" priority="2">
<xsl:call-template name="identity" />
<xsl:variable name="firstLink" select="preceding-sibling::node()[2]" />
<xsl:apply-templates select="key('kFloat', substring-after($firstLink/@href, '#'))/following-sibling::figure intersect key('kFloat', substring-after(@href, '#'))/preceding-sibling::figure" mode="float" />
<xsl:apply-templates select="key('kFloat', substring-after(@href, '#'))" mode="float" />
</xsl:template>
<xsl:template match="p/link[@href]">
<xsl:next-match />
<xsl:variable name="doc" select="/" />
<xsl:for-each select="for $ref in tokenize(@href, ' ') return substring-after($ref, '#')">
<xsl:apply-templates select="key('kFloat', ., $doc)" mode="float" />
</xsl:for-each>
</xsl:template>
<xsl:template match="figure" mode="float">
<float id="{@xml_id}"/>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltfiddle.liberty-development.net/ncdD7nu
Upvotes: 1