[xsl] Re: Creating sequence/range text from position

Subject: [xsl] Re: Creating sequence/range text from position
From: "Lizzi, Vincent vincent.lizzi@xxxxxxxxxxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Sat, 28 Jan 2023 05:50:00 -0000
Hi Charles,

Here is a slightly improved version that produces the expected output from
your sample input and also copes with a case of the id's listed in @rid not
being in sequential order.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
    xmlns:xs="http://www.w3.org/2001/XMLSchema";
    exclude-result-prefixes="xs"
    version="3.0">

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:key name="element-by-id" match="*[@id]" use="@id"/>

    <xsl:template name="label" as="element(label)">
        <xsl:param name="id" as="xs:string"/>
        <xsl:param name="context" as="node()" select="root(.)"/>
        <label><xsl:value-of select="1 + count(key('element-by-id', $id,
$context)/preceding::ref)"></xsl:value-of></label>
    </xsl:template>

    <xsl:template match="ref">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:call-template name="label">
                <xsl:with-param name="id" select="@id"/>
            </xsl:call-template>
            <xsl:apply-templates select="node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="xref[not(node())]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:variable name="context" select="root(.)"/>
            <xsl:variable name="labels" as="element(label)*">
                <xsl:for-each select="tokenize(@rid)">
                    <xsl:call-template name="label">
                        <xsl:with-param name="id" select="."/>
                        <xsl:with-param name="context" select="$context"/>
                    </xsl:call-template>
                </xsl:for-each>
            </xsl:variable>
            <xsl:variable name="sorted" as="element(labels)">
                <labels>
                    <xsl:for-each select="$labels">
                        <xsl:sort select="number()"/>
                        <xsl:sequence select="."/>
                    </xsl:for-each>
                </labels>
            </xsl:variable>
            <xsl:variable name="formatted" as="xs:string*">
                <xsl:for-each select="$sorted/label">
                    <xsl:choose>
                        <xsl:when test="not(preceding::label)">
                            <xsl:sequence select="string()"/>
                        </xsl:when>
                        <xsl:when test="number() - 1 eq
number(preceding::label[1])
                            and number() + 1 eq
number(following::label[1])"/>
                        <xsl:when test="number() - 1 eq
number(preceding::label[1])
                            and number() - 2 eq number(preceding::label[2])">
                            <xsl:sequence select="'-' || string()"/>
                        </xsl:when>
                        <xsl:when test="preceding::label">
                            <xsl:sequence select="',' || string()"/>
                        </xsl:when>
                    </xsl:choose>
                </xsl:for-each>
            </xsl:variable>
            <xsl:value-of select="string-join($formatted, '')"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Best wishes,
Vincent

_____________________________________________
Vincent M. Lizzi
Head of Information Standards | Taylor & Francis Group
vincent.lizzi@xxxxxxxxxxxxxxxxxxxx<mailto:vincent.lizzi@xxxxxxxxxxxxxxxxxxxx>




Information Classification: General
From: Charles O'Connor coconnor@xxxxxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Sent: Friday, January 27, 2023 7:05 PM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: [xsl] Creating sequence/range text from position

Hi all,

We use a word processor-like XML editor that presents users with generated
text for numbered bibliographic references and their in-text citations.
However, downstream systems require actual text to be placed. Question is, how
do we get it?

(Using SaxonEE 10.x)

Given

<article>
<p>Case 1 is <xref ref-type="bibr" rid="r2"/></p>
<p>Case 2 is <xref ref-type="bibr" rid="r1 r2 r3 r5 r6 r8 r9 r10"/></p>
<back>
<ref-list>
<ref id="r1">...</ref>
<ref id="r2">...</ref>
<ref id="r3">...</ref>
<ref id="r4">...</ref>
<ref id="r5">...</ref>
<ref id="r6">...</ref>
<ref id="r7">...</ref>
<ref id="r8">...</ref>
<ref id="r9">...</ref>
<ref id="r10">...</ref>
</ref-list>
</back>
</article>

We want

<article>
<p>Case 1 is <xref ref-type="bibr" rid="r2">2</xref></p>
<p>Case 2 is <xref ref-type="bibr" rid="r1 r2 r3 r5 r6 r8 r9
r10">1-3,5,6,8-10</xref></p>
<back>
<ref-list>
<ref id="r1">...</ref>
<ref id="r2">...</ref>
<ref id="r3">...</ref>
<ref id="r4">...</ref>
<ref id="r5">...</ref>
<ref id="r6">...</ref>
<ref id="r7">...</ref>
<ref id="r8">...</ref>
<ref id="r9">...</ref>
<ref id="r10">...</ref>
</ref-list>
</back>
</article>

(only the xrefs have changed)

Case 1 is pretty easily handled with:

<xsl:template match="xref[@ref-type='bibr']">
<xsl:variable name="rid" select="@rid" />
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:value-of
select="count(/article/back/ref-list/ref[@id=$rid]/preceding-sibling::ref) +
1" />
</xsl:copy>
</xsl:template>

But Case 2 is not even close. I tried adapting the solution here, to no avail:
https://stackoverflow.com/questions/47559712/xslt-sequence-of-numbers-to-rang
e<https://stackoverflow.com/questions/47559712/xslt-sequence-of-numbers-to-ra
nge>

Whatever I did, I just got the first number and none of the rest. The extra
twist here is that numbers should only be expressed as a range when 3 or more
occur sequentially.

Thanks,
Charles

Charles O'Connor l Lead Product Manager
Pronouns: He/Him
Aries Systems Corporation l www.ariessys.com<http://www.ariessys.com>
50 High Street, Suite 21 l North Andover, MA l 01845 l USA


Main: +1 (978) 975-7570
Cell: +1 (802) 585-5655

Current Thread