<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="../../../_utils/schema/yaps.rnc" type="application/relax-ng-compact-syntax"?>
<?xml-model href="../../../_utils/schema/yaps.isosch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
<?xml-stylesheet type="text/xsl" href="yaps2slidy1.xslt"?>
<!-- $Id: taste_of_Schematron.xml 51709 2026-05-28 23:29:13Z syd $ -->
<TEI xmlns="http://www.wwp.northeastern.edu/ns/yaps"
     xmlns:sch="http://purl.oclc.org/dsdl/schematron"
     version="5.0">
  <teiHeader>
    <fileDesc>
      <titleStmt>
        <title>Navigating the XML Tree</title>
        <author>Syd Bauman</author>
      </titleStmt>
      <editionStmt>
        <edition>TEI Summer School, University of Graz, Fri, 13 Sep 19</edition>
      </editionStmt>
      <publicationStmt>
        <distributor>Women Writers Project (via website)</distributor>
        <address>
          <addrLine>url:mailto:wwp@northeastern.edu</addrLine>
        </address>
        <date when="2019-09-13"/>
        <availability status="restricted">
          <p>Copyright 2019 Syd Bauman and the Women Writers Project</p>
          <p>This TEI-encoded XML file is available under the terms of
          the <ref target="https://creativecommons.org/licenses/by-sa/4.0/">Creative
          Commons Attribution-ShareAlike 4.0 International</ref>
          license.</p>
        </availability>
        <pubPlace>Boston, MA  USA</pubPlace>
      </publicationStmt>
      <sourceDesc>
        <p>Brief overview of Schematron covering
        rules, asserts, reports, and the <gi>value-of</gi> element</p>
      </sourceDesc>
    </fileDesc>
    <revisionDesc>
      <change who="#sbauman.emt" when="2019-09-12">Created, based on tree_navigation</change>
    </revisionDesc>
  </teiHeader>
  <text>
    <presentation>

      <section>
        <head>Schematron</head>
        <slide>
          <list>
            <item>First published in 1999 by an Australian named Rick Jelliffe</item>
            <item>Now an ISO Standard <hi rend="class(dull)">(ISO/IEC 19757-3:2016)</hi></item>
            <item>
              <cit>
                <quote>a rule-based validation language for making assertions about the presence or absence of patterns in XML trees.</quote>
                <bibl>Wikipedia</bibl>
              </cit>
            </item>
            <item>
              <cit>
                <quote>a feather duster to reach the corners that other schema languages cannot reach</quote>
                <bibl>Rick Jelliffe</bibl>
              </cit>
            </item>
          </list>
        </slide>
      </section>
      
      <section>
        <head>How it works, grossly oversimplified</head>
        <slide>
          <p>The basic underlying principle is simple (and yet masterfully clever):
          <q>Go to XPath A; from there test the existence of XPath B, and tell me
          if it is not there</q>.</p>
        </slide>
      </section>

      <section>
        <head>An example</head>
        <slide>
          <egMarkup scheme="TEI" type="valid">
            <sch:rule context="tei:person/tei:persName">
              <sch:assert test="@type">
                A person’s name as given in the contextual information
                about that person must be catagorized with @type.
              </sch:assert>
            </sch:rule>
          </egMarkup>
          <p>Note that this does not say anything about use of <att>type</att>
          on <gi>persName</gi> elements that are not inside <gi>person</gi>.</p>
        </slide>
      </section>
      
      <section>
        <head>Another example</head>
        <slide>
          <egMarkup scheme="Schematron" type="valid">
            <sch:rule context="tei:lg[ @type eq 'limerick']">
              <sch:assert test="count( child::tei:l ) eq 5">
                A limerick should have five metrical lines.
              </sch:assert>
            </sch:rule>
          </egMarkup>
        </slide>
      </section>
      
      <section>
        <head>How it works, oversimplified</head>
        <slide>
          <p>The basic underlying principle is simple (and yet masterfully clever):
          <q>Go to XPath A; from there test the existence of XPath B, and tell me
          if it is (not) there</q>.</p>
          <p>Since <q>there</q> means <code>true()</code> and <q>not there</q>
          means <code>false()</code>, XPath B does not even have to be
          a path to a node in the input document, it can be anything.</p>
          <p>And remember that an XPath returns a sequence of 0 or more nodes.
          If XPath A returns more than one node, XPath B is tested from each one
          in turn.</p>
        </slide>
      </section>
      
      <section>
        <head>Reversed logic example</head>
        <slide>
          <p>The attribute class <ref target="https://www.tei-c.org/release/doc/tei-p5-doc/en/html/ref-att.handFeatures.html">att.handFeatures</ref>
          provides the pair <att>scribe</att> &amp; <att>scribeRef</att> and the pair
          <att>script</att> &amp; <att>scriptRef</att>. It is common for a project to
          use one from each pair, but never the other. But you can well imagine an archive
          that accepts documents that uses either, but would not know how to process an
          element that actually had both attributes of a given pair.</p>
          <egMarkup scheme="Schematron" type="valid">
            <sch:rule context="tei:handNote|tei:handShift|tei:scriptNote|tei:typeNote">
              <sch:report test="@scribe and @scribeRef">
                The scribe should be identified with a token or with a
                pointer to a &amp;lt;person> element, but not both.
              </sch:report>
              <sch:report test="@script and @scriptRef">
                The script should be identified with a token or with a
                pointer to a &amp;lt;scriptNote> element, but not both.
              </sch:report>
            </sch:rule>
          </egMarkup>
        </slide>
      </section>
      
      <section>
        <head>Dynamicly generated information in messages</head>
        <slide>
          <egMarkup scheme="Schematron" type="valid">
            <sch:rule context="tei:handNote|tei:handShift|tei:scriptNote|tei:typeNote">
              <sch:report test="@scribe and @scribeRef">
                The scribe should be identified with a token or with a
                pointer to a &amp;lt;person> element, not both; but
                this <sch:value-of select="name(.)"/> has both.
              </sch:report>
              <sch:report test="@script and @scriptRef">
                The script should be identified with a token or with a
                pointer to a &amp;lt;scriptNote> element, not both; but
                this <sch:value-of select="name(.)"/> has both.
              </sch:report>
            </sch:rule>
          </egMarkup>
          <egMarkup scheme="Schematron" type="valid">
            <sch:rule context="tei:lg[@type eq 'limerick']">
              <sch:assert test="count(child::tei:l) eq 5"> A limerick should have five
              metrical lines, so either this line group should not be categorized as a
              limerick, or it should not have <sch:value-of select="count(child::l)"/>
              metrical lines.</sch:assert>
            </sch:rule>
          </egMarkup>
          <egMarkup scheme="Schematron" type="valid">
            <sch:rule context="tei:pb">
              <sch:assert role="warning" test="@n eq preceding::tei:pb[1]/@n + 1">
                The current page (<sch:value-of select="@n"/>) may be misnumbered!
              </sch:assert>
            </sch:rule>
          </egMarkup>
        </slide>
      </section>
      
      <section>
        <head>Remembering XPaths for later use</head>
        <slide>
          <egMarkup scheme="Schematron" type="valid">
            <sch:rule context="*[ @corresp ]">
              <sch:let name="ptr2me" value="concat('#', @xml:id )"/>
              <sch:report role="error" test="tokenize( normalize-space( @corresp ),' ') = $ptr2me">
                The <sch:value-of select="name()"/> element with id=<sch:value-of select="@xml:id"/>
                corresponds to itself!
              </sch:report>
            </sch:rule>
          </egMarkup>
          <egMarkup scheme="Schematron" type="valid">
            <sch:rule context="@xml:id">
              <sch:let name="thisID" value="normalize-space(.)"/>
              <sch:report test="../(ancestor::*|preceding::*)/@xml:id[ normalize-space(.) eq $thisID ]">
                The @xml:id "<sch:value-of select="$thisID"/>" on &amp;lt;<sch:value-of
                select="name(..)"/>> duplicates an @xml:id found earlier in the document</sch:report>
            </sch:rule>
          </egMarkup>
        </slide>
      </section>
      
      <section>
        <head>Where Schematron goes: ODD</head>
        <slide>
          <p>Ideally, your Schematron tests should be included (with all
          your other TEI customizations) in your TEI customization ODD file.</p>
          <egMarkup scheme="TEI" type="valid">
            <classSpec module="core" type="atts" ident="att.global" mode="change">
              <constraintSpec ident="xmlID-is-unique" scheme="schematron">
                <constraint>
                  <sch:rule context="@xml:id">
                    <sch:let name="thisID" value="normalize-space(.)"/>
                    <sch:report test="../(ancestor::*|preceding::*)/@xml:id[ normalize-space(.) eq $thisID ]
                                      ">The @xml:id "<sch:value-of select="$thisID" />" on &amp;lt;<sch:value-of
                                      select="name(..)"/>> duplicates an @xml:id found earlier in the document</sch:report>
                  </sch:rule>
                </constraint>
              </constraintSpec>
              <remarks>
                <p>The WWP does not currently use the XML <att
                scheme="xml">base</att> attribute. It is permitted by this
                schema so that validation post-XInclude processing
                works.</p>
              </remarks>
            </classSpec>
          </egMarkup>
        </slide>
      </section>

      <section>
        <head>When in ODD …</head>
        <slide>
          <p>In an ODD, Schematron rules, assertions, or reports appear in a
          <gi>constraint</gi> inside a <tag>constraintSpec
          scheme="schematron"</tag> which itself can appear in <gi>attDef</gi>,
          <gi>classSpec</gi>, <gi>elementSpec</gi>, or <gi>schemaSpec</gi>.</p>
          <p>If the <gi>sch:rule</gi> is missing (i.e., just bare <gi>sch:assert</gi> and
          <gi>sch:report</gi> elements), then the context is the element or attribute that is
          being defined.</p>
          <p>In an ODD, you need to specify the <code>tei:</code> prefix for TEI elements (even
          though you do not need to declare it).</p>
        </slide>
      </section>
      
      <section>
        <head>Another Schematron-in-ODD example</head>
        <slide>
          <egMarkup scheme="TEI" type="valid">
            <elementSpec module="core" ident="name" mode="change">
              <constraintSpec ident="subtype-matches-type" scheme="schematron">
                <constraint>
                  <sch:report test="@type eq 'person'  and  not( @subtype = ('officer','enlisted') )">
                    a name of a person should be sub-catagorized as an officer's name or an enlisted
                    man's name
                  </sch:report>
                  <sch:report test="@type eq 'ship'  and  not( @subtype = ('merchant','naval')">
                    the name of a ship should be sub-catagorized as that of a merchant or naval ship
                  </sch:report>
                </constraint>
              </constraintSpec>
              <attList>
                <attDef ident="type" mode="replace" usage="req">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef key="teidata.enumerated"/>
                  </datatype>
                  <valList type="closed">
                    <valItem ident="person"/>
                    <valItem ident="place"/>
                    <valItem ident="port"/>
                    <valItem ident="ship"/>
                  </valList>
                </attDef>
                <attDef ident="subtype" mode="replace">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef name="teidata.enumerated"/>
                  </datatype>
                  <valList type="closed">
                    <valItem ident="officer"/>
                    <valItem ident="enlisted"/>
                    <valItem ident="merchant"/>
                    <valItem ident="naval"/>
                  </valList>
                </attDef>
              </attList>
            </elementSpec>
          </egMarkup>
        </slide>
      </section>
      
      <section>
        <head>Where Schematron goes: standalone</head>
        <slide>
          <p>It is possible to use Schematron without ODD.</p>
          <egMarkup scheme="Schematron" type="valid" xmldecl="true">
            <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"
                        queryBinding="xslt2" >

              <sch:ns uri="http://www.tei-c.org/ns/1.0" prefix="tei"/>

              <sch:p><!-- prose description of set of tests --></sch:p>

              <sch:pattern>
                <!-- zero or more <sch:p> elements to describe pattern here -->
                <!-- zero or more <sch:let> elements here -->
                <!-- one or more <sch:rule> elements here -->
                <!--
                    each <rule> can have <p>s for a description, <let>s,
                    <assert>s, and <report>s.
                -->
              </sch:pattern>

            </sch:schema>
          </egMarkup>
        </slide>
      </section>
      
    </presentation>
  </text>
</TEI>
