Wolfgang Schindler
Wolfgang Schindler

Reputation: 28

Check numbering of list items with XSLT

I have got lists with two types of list markers, namely numeric (1., 2., 3., etc.) and alphabetic [a), b), c), etc. or aa), bb), cc), etc.]. I want to check whether each item of a list is numbered subsequently according to its list marker type.

This means in numeric lists a subsequent item should be numbered as n+1 plus a dot, where n is the number in the current item's term and n+1 would be the number in its following-sibling::*[1]/self::item/term.

In alphabetic lists the following-sibling::*[1]/self::item/term should contain the next letter(s) in alphabetical order from the current item/term plus a closing parenthesis. So for example after "b)" would follow "c)" and after "cc)" would follow "dd)".

I do not want to change the numbering using xsl:number, but just check and document the errors found and ideally their locations.

The input format would look like this:

<list>
  <item>
    <term>1.</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>3.</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>4.</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
</list>

Or:

<list>
  <item>
    <term>a)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>b)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
   <term>d)</term>
   <p>jhjhjh hjkjkjkj</p>
  </item>
</list>

Or:

<list>
  <item>
    <term>aa)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>cc)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
  <item>
    <term>dd)</term>
    <p>jhjhjh hjkjkjkj</p>
  </item>
</list>

These input samples deliberately contain errors that should be detected and located.

Upvotes: 0

Views: 49

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167716

Seems like a task for Schematron e.g.

<schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3">
    <pattern>
        <rule context="item[term[matches(., '^[0-9]+\.$')]][position() gt 1]">
            <assert test="
            let $number := term => substring-before('.') => xs:integer(), 
            $number2 := preceding-sibling::item[1]/term => substring-before('.') => xs:integer() 
            return $number = $number2 + 1">item with <value-of select="term"/> doesn't have the right number.</assert>
        </rule>
    </pattern>
</schema>

Sample fiddle.

For your alphabetic schema perhaps the following

<schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt3">
    <pattern>
        <rule context="item[term[matches(., '^[0-9]+\.$')]][position() gt 1]">
            <assert test="
            let $number := term => substring-before('.') => xs:integer(), 
            $number2 := preceding-sibling::item[1]/term => substring-before('.') => xs:integer() 
            return $number = $number2 + 1">item with <value-of select="term"/> doesn't have the right number.</assert>
        </rule>
        <rule context="item[term[matches(., '^[a-z]+\)$')]][position() gt 1]">
            <assert test="
            let $index := term => substring-before(')') => string-to-codepoints(), 
            $index2 := preceding-sibling::item[1]/term => substring-before(')') => string-to-codepoints()
            return every $bool in for-each-pair($index, $index2, function($a, $b) { $a = $b + 1 }) satisfies $bool">item with <value-of select="term"/> doesn't have the right term.</assert>
        </rule>
    </pattern>
</schema>

helps, sample fiddle is online.

As for the error with Schxslt2, I think it is a bug in the template schxslt:failed-assertion-content of transpile.xsl that can be fixed as

  <xsl:template name="schxslt:failed-assertion-content" as="node()+">
    <xsl:sequence select="@flag"/>
    <xsl:sequence select="@id"/>
    <xsl:sequence select="@role"/>
    <xsl:attribute name="test" select="@test => replace('\{', '{{') => replace('\}', '}}')"/>
    <xsl:attribute name="xml:lang" select="schxslt:in-scope-language(.)"/>
    <alias:attribute name="location" select="{($schxslt:location-function, 'path')[1]}(.)" xsl:use-when="not($schxslt:streamable) or exists($schxslt:location-function)"/>
    <xsl:call-template name="schxslt:report-diagnostics"/>
    <xsl:call-template name="schxslt:report-properties"/>
    <xsl:call-template name="schxslt:report-message"/>
  </xsl:template>

Bug is already fixed in Schxslt2 1.2.2, I have incorporated that version as an option in the Schematron fiddle, the sample now works.

Upvotes: 1

Related Questions