xsltschematron

Schematron rule to check if files have corresponding elements


I have a file structure like this:

bookmap.ditamap
├── en-US/
│   └── CTR_MyProduct.ditamap
├── es-ES/
│   └── CTR_MyProduct.ditamap
└── fr-FR/
    └── CTR_MyProduct.ditamap

The content of bookmap.ditamap is:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookmap PUBLIC "-//OASIS//DTD DITA BookMap//EN" "bookmap.dtd">
<bookmap>
    <booktitle><mainbooktitle/></booktitle>
    <part>
        <mapref href="en-US/CTR_Product.ditamap"/>
    </part>
    <part>
        <mapref href="fr-FR/CTR_Product.ditamap"/>
    </part>
</bookmap>

I'd like to have a Schematron rule which should crawl through the subdirectories looking for files starting with CTR_ and ending with .ditamap which complains if there is no corresponding <part> element, like:

<part>
  <mapref href="xx-YY/CTR_Product.ditamap"/>
</part>

In this example, there is no <part> element for the Spanish (es-ES) map. This should be reported. Do you think this is possible to validate in Schematron?


Solution

  • With underlying XSLT/XPath 3 support and Saxon 9 or 10 or 11 you can probably do e.g.

    every $uri in uri-collection('?select=CTR_Product.ditamap;recurse=yes') 
    satisfies 
      some $part in part 
      satisfies contains($uri, $part/mapref/@href)
    

    If those elements are in a namespace you will need to set that up too as the default XPath selection namespace.

    You can also use wildcards e.g. uri-collection('?select=CTR_*.ditamap;recurse=yes').

    Perhaps using ends-with($uri, $part/mapref/@href) instead of contains($uri, $part/mapref/@href) is a better way to check.

    More complete in the context of Schematron:

        <sch:rule context="bookmap">
            <sch:let name="parts" value="part"/>
            <sch:let name="uris" value="uri-collection('?select=*.ditamap;recurse=yes')"/>
            <sch:assert test="every $uri in $uris
                satisfies 
                some $part in part 
                satisfies ends-with($uri, $part/mapref/@href)">
                Not every URI exists. 
                <sch:value-of select="$uris[not(some $ref in $parts/mapref satisfies ends-with(., $ref/@href))]"/>
            </sch:assert>  
        </sch:rule>