<?xml version="1.0" encoding="UTF-8"?>
<TEI xmlns:sch="http://purl.oclc.org/dsdl/schematron" xmlns="http://www.tei-c.org/ns/1.0" version="4.1.0">
  <!-- For information on how to build HTML output, see ./checking_pointers_in_ODD.txt. -->
  <teiHeader>
    <fileDesc>
      <titleStmt>
        <title>Checking Pointers Using your TEI ODD Customization</title>
        <author xml:id="sb">
          <persName>Syd Bauman</persName>
        </author>
        <editor>
          <persName>Sarah Connell</persName>
        </editor>
        <respStmt>
          <resp>Additional technical review</resp>
          <persName>Elisa Beshero-Bondar</persName>
          <persName>Lou Burnard</persName>
          <persName>Ash Clark</persName>
          <persName>Martin Holmes</persName>
          <persName>David Maus</persName>
          <persName>Helena Bermúdez Sabel</persName>
          <persName>Martina Scholger</persName>
          <persName>Raffaele Viglianti</persName>
        </respStmt>
      </titleStmt>
      <editionStmt>
        <edition>1.2.1</edition>
      </editionStmt>
      <publicationStmt>
        <publisher>
          <orgName>Northeastern University WWP</orgName>
        </publisher>
        <availability>
          <licence target="https://creativecommons.org/licenses/by/4.0/">
            This work is available under the terms of the Creative
            Commons Attribution 4.0 International (CC BY 4.0)
          </licence>
          <p>Users should feel free to copy portions of this document
          for use in their own TEI customizations, with or without
          modification. The only expectation (and legal burden) is to
          explicitly state that you have done so (e.g., in the prose
          of your ODD or in a comment), preferably including the URL
          of this file.</p>
        </availability>
      </publicationStmt>
      <sourceDesc>
        <ab>Born digital.</ab>
      </sourceDesc>
    </fileDesc>
    <encodingDesc>
      <projectDesc>
        <ab>See <ref
        target="https://github.com/TEIC/TEI/issues/1675">issue
        #1675</ref> against the TEI Guidelines rep.</ab>
      </projectDesc>
    </encodingDesc>
    <revisionDesc>
      <change n="1.2.1" who="#sb" when="2024-05-26">
        Tiny tweaks
      </change>
      <change n="1.2.0" who="#sb" when="2024-05-25">
        Added several <gi>sch:rule</gi> elements (with
        <att>context</att>s) as the TEI will soon be requiring them.
      </change>
      <change n="1.1.0" who="#sb" when="2022-09-22">
        Changed from a TEI document to a WWP document. For the
        previous revision history look in TEI Consortium’s
        <name>Documentation</name> repository. (I.e., <ptr
        target="https://github.com/TEIC/Documentation.git"/>.) It was
        called <name>tcw32</name>, and lived in the <name>TCW/</name>
        directory.
      </change>
      <change n="1.0.0" who="#sb" when="2022-09-01">
        Having made lots of various minor improvements over the past
        year, and incorporated changes recommended by my colleagues
        Sarah Connell and Ash Clark, and from Martina Scholger, I
        think this is finally ready for initial release. Thus this
        version is considered edition 1.0.0.
      </change>
      <change who="#sb" when="2021-08-15">
        Tweaks to make public (e.g., remove references to file paths
        on my local system)
      </change>
      <change who="#sb" when="2021-08-01">
        resume work, having last edited 2021-02-28T23:59.
      </change>
      <change who="#sb" when="2021-01-18">
        started as a customization ODD on my local system (home
        desktop — we’re in the middle of a pandemic)
      </change>
    </revisionDesc>
  </teiHeader>
  <text>
    <front>
      <titlePage>
        <docAuthor>Syd Bauman</docAuthor>
        <docTitle>
          <titlePart>
            <title type="main">Customizing TEI to Check Pointers</title>
          </titlePart>
        </docTitle>
        <docImprint>
          <publisher>Northeastern University Women Writers Project</publisher>
        </docImprint>
        <docDate>
          <date when="2022">2022</date>
        </docDate>
      </titlePage>
      <divGen type="toc"/>
      <div type="section">
        <head>Acknowledgments</head>
        <p>The author is indebted to Sarah Connell for editing the
        prose, and to the following people for testing the Schematron.
        <list>
          <item>Elisa Beshero-Bondar</item>
          <item>Lou Burnard</item>
          <item>Ash Clark</item>
          <item>Martin Holmes</item>
          <item>David Maus</item>
          <item>Helena Bermúdez Sabel</item>
          <item>Martina Scholger</item>
          <item>Raffaele Viglianti</item>
        </list>
        Without their help this document might not exist, and if it
        did, it would be harder to read and would have a lot more
        mistakes.</p>
      </div>
      <div type="section">
        <head>Introduction</head>
        <p>As with any other part of document production, when
        using URIs (web pointers) it is advantageous to check
        that they <soCalled>work</soCalled> early in the production
        process. The aspects of a pointer we would want to test, in
        increasing order of difficulty, are that the URI:
        <list rend="numbered">
          <item n="1">points to <emph>something</emph> (i.e., has proper syntax)</item>
          <item n="2">points to something that can be retrieved (i.e.,
          refers to an object that exists and to which the user has
          access)</item>
          <!-- Note: <ref> in next item is not rendered properly by
               bin/teitohtml; see
               https://github.com/TEIC/Stylesheets/issues/535 -->
          <item n="3">points to the right <emph>kind</emph> of thing (e.g., the <att>ref</att> of <gi>persName</gi> should point to a <gi>person</gi>, not a <gi>fileDesc</gi> nor an SVG chart of COVID-19 cases)</item>
          <item n="4">points to the right thing (e.g., the
          <val>#MLK</val> of <code>&lt;persName ref="#MLK">Martin
          Luther King, Jr.&lt;/persName></code> should point to an
          entry for Martin Luther King, Jr., not an entry for Lois
          Lane).</item>
        </list></p>
        <p>Most of the main closed schema languages, including PureODD
        and RELAX NG (but excluding DTDs), have the capability to test
        that an information item (in our case, and thus hereafter, an
        attribute value) meets the syntactic constraints of a URI.
        That is, we can test (1) above just by using our schemas. But
        none of those languages (including PureODD without use of
        <gi>constraintSpec</gi>) have the capability to test
        (2)–(4).</p>
        <p>The open schema language used by TEI (ISO Schematron) does
        allow for (2) and (3). However, a) it is difficult to
        associate the needed tests with all <name
        type="class">teidata.pointer</name> attributes, and b) it
        requires special-case coding for (3), depending on what is
        considered <soCalled>right</soCalled> in the current
        circumstances. In any case, (4) is much more difficult, and
        usually requires a human, if not a human with domain-specific
        knowledge.</p>
        <p>Thus the TEI Technical Council does not plan to include any
        general tests for (2)–(4) directly in TEI P5, at least not in
        the near future. However, recognizing that at least (2) &amp;
        (3) represent an important subset of validation tests that
        projects want to perform, we exemplify herein some mechanisms
        for doing so. The intent is that projects can copy-and-paste
        ODD fragments from this file into their own, and modify as
        desired to suit their local needs.</p>
        <p>Note, however, that this document only discusses testing of
        URIs that are intended to return an XML document or fragment
        thereof. It is possible to use URIs to point to and retrieve
        other sorts of information objects including JSON, images,
        HTML (other than XHTML), audio files, word processor files,
        etc., but these cases are not considered herein.</p>
        <p>Also note that the examples in this document often use an
        attribute as the context node of an ISO Schematron rule. Some
        Schematron processors fail to process this correctly. (The
        oXygen XML Editor handles these perfectly well, as does David
        Maus’s <ref
        target="https://github.com/schxslt/schxslt/releases/latest">SchXslt</ref>.)</p>
      </div>
    </front>
    <body>
      <p>There are over 100 attributes in TEI defined as <name
      type="class">teidata.pointer</name>.<note place="foot">And another 4 or so
      defined as <name type="class">teidata.namespace</name>, whose
      values have the same syntax as a URI, but are not necessarily
      intended to point at anything.</note> Roughly 60 of those are
      restricted to having only 1 URI as a value, but the other nearly
      50 permit multiple URIs in the value. Processing multiple
      pointers is much more difficult than handling a single URI, and
      thus the singleton cases are considered separately from the cases
      in which multiple URI values of a single pointer attribute need 
      to be tested.</p>
      <p>Cases where a single item is being pointed to directly are
      considered first, then cases where multiple items may be pointed
      at directly by multiple URIs expressed as the value of a single
      attribute, and then then cases where an item is being referred
      to indirectly either via an intermediate <gi>link</gi> or
      <gi>alt</gi> element, or via a prefix defined in the
      <gi>prefixDef</gi>. Lastly, a method of ensuring that validation
      is being performed <emph>after</emph> XInclude processing is
      demonstrated.</p>
      <div type="section">
        <head>Direct reference by single value</head>
        <!--
            To get a list of attributes that are defined as singleton URIs try
            $ xsel -t -m "//t:datatype[not(@maxOccurs) or @maxOccurs='1'][t:dataRef[not(@maxOccurs) or @maxOccurs='1'][@key='teidata.pointer']]" -v "concat( ancestor::t:elementSpec/@ident|ancestor::t:classSpec/@ident|ancestor::t:dataSpec/@ident,'/@',parent::t:attDef/@ident)" -n /path/to/local/P5/p5subset.xml | sort
            on Syd’s machine.
        -->
        <p>Projects that intend to always use one and only one URI as
        the value of an attribute which by default in TEI may take
        multiple URIs will probably find it best to constrain said
        attribute to only 1 URI. For example, if the (fictional)
        project <q>The Papers of Dr. Virgil Swann</q> were to provide
        correspondence links between Dr. Swann’s English translation
        of and his transcription of an intercepted Kryptonian message,
        each <gi>s</gi> element with an <att>xml:lang</att> of
        <val>en</val> would bear a <att>corresp</att> attribute that
        pointed to the corresponding <gi>s</gi> element with an
        <att>xml:lang</att> of <val>x-kr</val>. The TEI
        <att>corresp</att> attribute allows one or more URIs. But for
        this project, the <att>corresp</att> of <gi>s</gi> should be
        limited to only 1 URI. This can be accomplished as shown in
        <ptr target="#eg01"/>.
        </p>
        <div type="subsection">
          <head>shorthand pointer fragment identifier</head>
          <p>The <term>fragment identifier</term> portion of a URI is
          that which follows the first <code>#</code> (read
          left-to-right). The portion of the URI before the first
          <code>#</code> locates the document of interest; the portion
          after the first <code>#</code> locates the element of
          interest within said document. The portion after the first
          <code>#</code> can take many forms, only one of which is
          considered in this document: the <term>shorthand
          pointer</term> fragment identifier. It is probably by far
          the most common form of fragment identifier; it refers to an
          element in the document in question by referring to its
          ID. (In TEI the ID is always indicated by the
          <att>xml:id</att> attribute.) This typically looks like
          either <eg>https://www.w3.org/TR/xptr-framework/#<emph
          style="font-weight:bold; font-style:
          normal;">shorthand</emph></eg> or <eg>#<emph
          style="font-weight:bold; font-style:
          normal;">DeRoseetal1990</emph></eg><!--. ← Sentence ending
          period shows up at beginning of next line. I claim this is a
          Stylesheets problem. But it is not one I am confident I can
          fix at all, let alone in the next few minutes, so I have
          just commented it out. (BTW, not really that hard to fix,
          but hard to fix and be confident you haven’t broken
          something else.) —Syd, 2022-09-01 --> If there is no
          document mentioned to the left of the first <code>#</code>,
          then the document being referred to is the base document,
          which is typically the current document in which the URI
          appears (but which can be modified by the
          <att>xml:base</att> attribute).</p>
          <p>First, just to demonstrate how this works in general,
          example <ptr target="#eg02"/> shows how to test that the
          <att>ref</att> attribute of <gi>g</gi> has the correct
          syntax to point to <emph>something</emph> in the same file.
          However, as this check can be expressed in PureODD without
          resorting to Schematron, a PureODD-only mechanism is
          exemplified in <ptr target="#eg03"/>; it has the sizable
          advantage that the testing is performed by the closed schema
          language. Note, however, that although this use of the
          <att>restriction</att> attribute is supported by the TEI’s
          current ODD processor, other uses may not be; in particular,
          <att>restriction</att> is currently only usable with the
          <att>name</att> attribute, not the <att>key</att> attribute
          of <gi>dataRef</gi>.</p>
          <p>To check that a shorthand pointer fragment identifier URI
          points to <emph>something</emph> that can be retrieved
          (i.e., to test a pointer that looks like <code>#duck</code>
          for (2), above) you can take advantage of the XPath
          <code>id()</code> function. This technique is used in <ptr
          target="#eg04"/> to ensure that the <att>ref</att> of a
          <gi>g</gi> actually points to something in the current
          document. A call to <code>id('tho')</code> returns the
          element from the same document, if there is one, that bears
          an <att>xml:id</att> of <val>tho</val>. There is supposed to
          be at most one such element; if there were two or more, only
          the first is returned. Thus <code>id('tho')</code> may be
          thought of as <code>//*[@xml:id eq 'tho'][1]</code><note
          place="foot">Except there are differences in whitespace
          normalization.</note>.</p>
          <p>To check that a shorthand pointer fragment identifier URI
          specifically points to a particular element type (in this
          case <gi>char</gi> or <gi>glyph</gi>), you can simply append
          a node test, as seen in <ptr target="#eg05"/>. Note that
          references to TEI elements in the XPath expressions in the
          Schematron inside <gi>constraint</gi> need to be explicitly
          bound to the TEI namespace. Although an ODD author may
          define any namespace prefix for the purpose (using the
          Schematron <gi>ns</gi> element), TEI ODD software will
          automatically insert a definition that binds the prefix
          <code>tei:</code> to the TEI namespace.</p>
        </div>
        <div>
          <head>local filesystem</head>
          <p>A URI may refer to a file on the local filesystem using
          either an <term>absolute-path reference</term> or a
          <term>relative-path reference</term>.<note place="foot">A
          URI may also refer to a file on the local filesystem using
          the <term>file:</term> URI scheme. This document does not
          consider these URIs, except in a demonstration that testing
          that a URI uses the <code>file:</code> scheme is possible
          (in <ptr target="#eg58"/>) and the following comment on the
          general syntax of a <term>file:</term> scheme URI — Said
          format is <code>file://host/path</code>, where the
          <mentioned>//host</mentioned> portion is optional
          (defaulting to <q>localhost</q>), or may be expressed as
          just <mentioned>//</mentioned> (again defaulting to
          <q>localhost</q>). Thus <code>file:/path</code> and
          <code>file:///path</code> are both perfectly acceptable ways
          to refer to the file found at <mentioned>path</mentioned> on
          the local filesystem.<!--
          https://en.wikipedia.org/wiki/File_URI_scheme --></note> An
          absolute-path reference starts with a slash; a relative-path
          reference does not.<!--
          https://tools.ietf.org/html/rfc3986#section-4.2 --> A
          relative-path reference <emph>may</emph> start with a dot
          segment (<code>./</code>), unless the first segment of its
          path contains a colon, in which case it <emph>must</emph>
          start with a dot segment.</p>
          <p>The URI that refers to a file on the local filesystem may
          have a <code>#</code> followed by a fragment identifier.
          The scenarios covered here are, in order, testing that a
          URI: points to a local file, points to a local file with a
          particular file extension, points to a local file with a
          particular root element, and last that a URI points to a
          particular element type in a local file.</p>
          <div>
            <head>entire local file</head>
            <p>A <gi>moduleRef</gi> element is typically used to refer
            to one of the TEI modules (for example, <val>core</val>,
            <val>gaiji</val>, or <val>namesdates</val>) from a
            customization ODD file using its <att>key</att> attribute.
            But <gi>moduleRef</gi> can also refer to a non-TEI module
            using its <att>url</att> attribute. The TEI schema ensures
            that the value of the <att>url</att> attribute is, in
            fact, a URI (that is, it performs test (1)). It would be
            quite reasonable for a project to want to check that the
            value of <att>url</att> was a URI that referred to an
            existing, readable, local XML file.</p>
            <p>It is possible to write generic constraints for this
            purpose that would allow <emph>any</emph> valid URI that
            referred to a local file (whether an absolute path
            reference, a relative path reference, or a
            <name>file</name> URL scheme; see <ptr target="#eg58"/>)
            or that a value uses a private URI scheme prefix defined
            by a <gi>prefixDef</gi> (see <ptr target="#eg59"/>).
            However, in most cases projects would probably want to
            constrain the value to a particular method for referring
            to an existing, readable, local XML file (if not a
            particular file, for which see <ptr target="#eg06"/>). For
            example, <ptr target="#eg07"/> demonstrates a method for
            requiring that the <att>url</att> attribute of
            <gi>moduleRef</gi> refers to a local file that is in the
            same directory as the instance ODD and whose filename ends
            in <val>.rng</val> (and thus is probably a RELAX NG file
            in the XML syntax).</p>
            <p>There is often good reason to ensure that the file
            referred to (whether local or remote) is readable,
            well-formed XML. Luckily, XPath provides the
            <code>doc-available()</code> function for this very
            purpose. The constraints demonstrated in <ptr
            target="#eg08"/> first ensure that the information item
            referred to by the <att>url</att> attribute is a
            <emph>file</emph>, not an <emph>element</emph>, and then
            require that the file be readable, well-formed XML. The
            example at <ptr target="#eg09"/> duplicates these
            constraints, and also tests that the outermost element of
            the retrieved file is an <gi>rng:grammar</gi> element.</p>
            <!-- 
            <p>To test that a value uses the proper
            syntax to refer to a local file we need to perform
            explicit tests. There are three possible syntaxes: an
            absolute path reference, a relative path reference, or a
            <name>file</name> URL scheme. Testing for an absolute path
            reference or a <name>file</name> URL scheme is trivial,
            and can be performed by simple comparison. Testing for a
            relative path reference is slightly more complex, as we
            need to check for the lack of a particular character (a
            colon), and thus is much easier to do with a regular
            expression. So the XPath <code>starts-with( $val, './' )
            or starts-with( $val, 'file:' ) or matches( $val,
            '([A-Za-z0-9._~!$&amp;'()*+,;=@-]|(%[0-9A-Fa-f][0-9A-Fa-f]))+'
            )</code>
            <p>Luckily, XPath provides the
            <code>doc-available()</code> function to test whether an
            XML file is readable. Thus, as demonstrated in <ptr
            target="#eg07"/>, the constraint required is relatively
            simple.</p> -->
          </div>
          <div>
            <head>element of a local file</head>
            <p>The <att>ref</att> attribute of <gi>persName</gi>
            should typically point to a <gi>person</gi> element, which
            will often be in a separate
            <soCalled>personography</soCalled> file. Presuming that
            file is in a known location in the local filesystem, <ptr
            target="#eg10"/> can be used to test that the <att>ref</att>
            attribute refers to a <gi>person</gi> in that file.</p>
          </div>
      </div>
      <div>
        <head>remote pointers</head>
        <p>Checking remote pointers is in principle very similar to
        checking local pointers, but with different tests on the
        syntax of the URI. (They are also, of course, a bit harder to
        test because you must have a working internet connection (or
        alternatively use an <ref
        target="https://xmlcatalogs.org/catalogs-1.1.html">XML
        Catalog</ref>), and have to worry about firewalls, proxies,
        same-source problems, cached files, etc.)  For example, <ptr
        target="#eg11"/> ensures that any <gi>equiv</gi> element has a
        <att>uri</att> attribute, and further that said attribute
        refers to the (fictional) <q>markup_taxonomy</q> page of the
        WWP website, allowing a reference to that page on either the
        production or test version of the website, and allowing access
        with or without specifying a secure connection. Note that this
        example only checks the <emph>syntax</emph> of the
        <att>uri</att> attribute, and does not ensure that there
        actually is such a page or specific element on that page.</p>
        <p>The <ptr target="#eg12"/> example, on the other hand,
        ensures that the <att>filter</att> attribute of <gi>equiv</gi>
        points to an XSLT program. It does this by testing that either
        the namespace of the outermost element of the retrieved file
        is the XSLT namespace (because the outermost element of a
        <soCalled>normal</soCalled> XSLT program could be either
        <gi>xsl:stylesheet</gi> or <gi>xsl:transform</gi>), or that
        the outermost element has a <att>xsl:version</att> attribute
        (because a <ref
        target="https://www.w3.org/TR/2017/REC-xslt-30-20170608/#simplified-stylesheet">simplified
        stylesheet</ref> must have such an attribute).  Thus, in order
        for a <att>filter</att> attribute to pass this test, it not
        only needs the right syntax, but it must also point to an XSLT
        program that exists and is accessible via the web.</p>
      </div>
    </div>
    <div>
      <head>Direct access by multiple values</head>
      <p>If a pointer attribute may have multiple values, testing is
      mildly more difficult because the attribute needs to be parsed
      first. In the general case, delivering a precise error message
      is quite difficult,
      <!-- 
           Comment by dmj:
           
           I don’t agree. Let’s take the example in figure 14. You could rewrite it as:
           +- - - - - -
           | <sch:let name="WITs" value="tokenize( normalize-space(.), ' ')"/>
           | <sch:let name="WITs_invalid" value="filter(function ($WIT) { not(starts-with($WIT, '#')) }, $WITs)"/>
           | <sch:assert test="empty($WITs_invalid)">
           |   The following pointers in this @wit attribute do not start with a '#': <sch:value-of select="$WITs_invalid"/>
           | </sch:assert>
           +- - - - - -
      -->
      as the entire process needs to be handled in XPath because
      Schematron does not have an iteration construct. However,
      specific cases may be reasonably easy to handle. <ptr
      target="#eg13"/> tests that a <att>rendition</att> attribute
      refers to at most 2 <gi>rendition</gi> elements in the same
      file. This is not particularly difficult because of the
      restriction that there are at most 2 pointers.</p>
      <p>But in many, if not most, cases there is no such restriction.
      For example, there may be dozens of witnesses to a particular
      manuscript manifestation. <ptr target="#eg14"/> tests that each
      pointer in the value of a <att>wit</att> refers to a
      <gi>witness</gi> element in the same file. In addition to tests
      similar to the previous example, this example reports to the
      user <emph>what</emph> each pointer in a failed value is, in
      fact, pointing to.</p>
    </div>
    <div>
      <head>Possibly indirect reference</head>
      <p><ptr target="#eg10"/> demonstrates a methodology for ensuring
      that the <att>ref</att> of <gi>persName</gi> points to a
      <gi>person</gi>. One recommended method for encoding an
      ambiguous reference to a person is to use the <gi>alt</gi>
      element — the encoding of the ambiguous reference itself points
      to an <gi>alt</gi> element which, in turn, points to each of the
      possible <gi>person</gi>s. (I.e., as if the value of
      <att>evaluate</att> were <val>one</val>. See <ptr
      target="https://wwp.northeastern.edu/outreach/seminars/_current/presentations/contextual_encoding/advanced_context_09.xhtml"/>
      for a sample encoding.) Similarly, a reference to multiple
      individuals could be encoded as multiple pointers on a single
      <att>ref</att>, or could be encoded as a single pointer on the
      <att>ref</att> that points to a <gi>link</gi> element which, in
      turn, points to each <gi>person</gi> referred to. One advantage
      to this latter method is that you can restrict the
      <att>ref</att> of <gi>persName</gi> to one and only one pointer,
      and use similar constraints for both ambiguous and multiple
      references. <ptr target="#eg15"/> demonstrates such
      constraints.</p>
      <p>A similar set of constraints can be expressed in a simpler,
      perhaps easier to follow, way by splitting them up as constraints
      on the <att>ref</att> of <gi>persName</gi> and the
      <att>target</att> of <gi>link</gi> and <gi>alt</gi> separately,
      as demonstrated in <ptr target="#eg16"/>. Because the code for
      <gi>alt</gi> and <gi>link</gi> is somewhat complicated, this
      technique expresses that code only once as an abstract
      Schematron pattern, which is instantiated separately, once for
      <gi>alt</gi> and once for <gi>link</gi>.</p>
    </div>
    <div>
      <head>Reference via <gi>prefixDef</gi></head>
      <p>The TEI provides a method of indirection for both shortening
      URLs and having a single place to change a set of URIs. This
      mechanism makes use of a local URL prefix and a definition of
      how that prefix is mapped to a full URI which is expressed in a
      <gi>prefixDef</gi> element. Creating pointers using this method
      is simpler, shorter, easier to read (and thus proofread), and
      reduces the chance for errors in the first place. Checking
      pointers that use this method, however, is significantly more
      difficult. As with several other types of pointer checking, it
      is the general case that is most difficult; particular cases may
      be reasonably easy.</p>
      <p>The scenario considered here is the simple (and to my
      knowledge far most common) case in which the
      <att>matchPattern</att> follows the shorthand pointer syntax or
      a subset thereof, and the <att>replacementPattern</att> follows
      the syntax of a URL except it ends with <code>#$1</code>. I.e.,
      the case in which the matched bit is the shorthand pointer, as
      seen in an <ref
      target="https://www.tei-c.org/raelease/doc/tei-p5-doc/en/html/SA.html#index-egXML-d53e125265">example
      from the <title>Guidelines</title></ref>. While example <ptr
      target="#eg17"/> limits itself to <gi>prefixDef</gi>s of this
      sort, it does not limit itself to any particular prefix. The
      document is searched for possible prefix values.</p>
    </div>
    <div type="section">
      <head>XInclude, yet?</head>
      <p>It is often the case that it is known in advance that certain
      tests will fail before XInclude processing, whereas we hope they
      would succeed after XInclude processing.<note place="foot">In
      the OXygen XML editor, you can toggle whether XInclude
      processing is performed before validation using the <q>Enable
      XInclude Processing</q> checkbox in the Options > Preferences >
      XML > XML Parser pane.</note> For example, imagine that at our
      <q>The Papers of Dr. Virgil Swann</q> project the extant
      documents to be encoded (which include letters, scientific
      notebooks, satellite schematics, a dictionary, and translations
      of intercepted radio transmissions) have been categorized using
      a project-specific taxonomy.  This taxonomy is encoded as a
      <gi>taxonomy</gi> element. Since each TEI document refers to
      this taxonomy (from its
      <code>/TEI/teiHeader/profileDesc/textClass/catRef/@target</code>),
      the project has chosen to have a copy of the entire project
      taxonomy in each TEI document’s header (in its
      <code>/TEI/teiHeader/encodingDesc/classDecl</code>). In order to
      avoid multiple copies of the same information, the project has
      chosen to store the <gi>taxonomy</gi> in a separate document and
      use XInclude to insert it into each TEI header.<note
      place="foot">Why they would choose to do this rather than use a
      URI that points to an external document is an interesting, if
      irrelevant, question. They may well have been influenced by the
      fact that <emph>every</emph> example of <gi>catRef</gi> in the
      <title>Guidelines</title> uses
            <!--
                Comment by DMJ:
                An observation —

                If we have a file taxonomy.xml that defines a
                <category xml:id="cat">, and we have a file
                document.xml that uses XInclude to include
                taxonomy.xml, and has <catRef target="#cat"/>, then
                the reference "#cat" is a dangling reference *even
                after XInclude is performed*.

                The reason is, that XInclude performs XML Base URI
                Fixup (Cf. XInclude specs) and modifies the base URI
                of the included taxonomy.

                id() works because it does *not* rely on URIs but
                ID/IDREF. 
            -->
            shorthand pointers.</note></p>
      <p>Given this situation, imagine now that the project wishes to
      check that the <att>target</att> attributes actually point to
      one or more <gi>category</gi> elements. This is problematic,
      because the <gi>category</gi> elements are not actually in the
      file as it sits unprocessed. There are a variety of ways this
      could be handled, probably the easiest of which is simply to
      skip the test (and warn the user it is being skipped) if
      XInclude processing has not yet taken place.</p>
      <p>There are two straightforward methods of asking the question
      <q>has XInclude processing taken place yet?</q>. The first
      relies on the fact that after XInclude processing, no elements
      from the XInclude namespace should remain: they should have
      become the file to be included or, in case of error, the
      contents of the <gi>xi:fallback</gi>. This method is exemplified
      in <ptr target="#eg18"/>.</p>
      <p>The second relies on the fact that before XInclude processing
      the file does not have a <gi>taxonomy</gi>, and after XInclude
      processing it does. This method is exemplified in <ptr
      target="#eg19"/>.</p>
    </div>
    </body>
    <back>
      <div type="meat_of_the_matter">
        <head>Examples</head>
        <!-- ========= figure eg01 ========= -->
        <figure xml:id="eg01">
          <head>PureODD to limit attribute to one URI</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="only_1_ptr">
            <elementSpec module="analysis" ident="s" mode="change">
              <attList>
                <attDef ident="corresp" mode="change">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef key="teidata.pointer"/>
                  </datatype>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>In this example the <att>corresp</att> of <gi>s</gi> is
          altered so that its value must be one and only one URI.</p>
        </figure>
        <!-- ========= figure eg02 ========= -->
        <figure xml:id="eg02">
          <head>Ensure <att>ref</att> of <gi>g</gi> is a shorthand pointer</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="ref_of_g_is_shorthand_ptr">
            <elementSpec ident="g" module="gaiji" mode="change">
              <attList>
                <attDef ident="ref" mode="change">
                  <constraintSpec ident="g_has_shorthand_ref" scheme="schematron">
                    <desc>Somewhat useless example that just tests
                    that the <emph>syntax</emph> of the <att>ref</att>
                    attribute of <gi>g</gi> is nothing but a shorthand
                    pointer, e.g. <val>#duck</val>.</desc>
                    <constraint>
                      <sch:rule context="tei:g/@ref">
                        <sch:let name="ref" value="normalize-space(.)"/>
                        <sch:assert test="matches( $ref, '#\i\c*' )">
                          The value of the ref= attribute of 'g' ("<sch:value-of select="."/>") is supposed to be a shorthand pointer; i.e., should look like '#duck'.
                        </sch:assert>
                      </sch:rule>
                    </constraint>
                  </constraintSpec>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>Snippet of ODD code that uses Schematron to enforce that
          the value of <att>ref</att> of <gi>g</gi> is a shorthand
          pointer. The regular expression <code>\i\c*</code> matches
          an <ref target="https://www.w3.org/TR/REC-xml/#NT-Name">XML
          Name</ref>, and thus <code>#\i\c*</code> matches a shorthand
          pointer to an XML element with an ID attribute.</p>
        </figure>
        <!-- ========= figure eg03 ========= -->
        <figure xml:id="eg03">
          <head>Ensure <att>ref</att> of <gi>g</gi> is a shorthand pointer, PureODD</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg03_egXML">
            <elementSpec module="gaiji" ident="g" mode="change">
              <attList>
                <attDef mode="replace" ident="ref" usage="req">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef key="xmpdata.shorthandPointer"/>
                  </datatype>
                  <remarks>
                    <p>This attribute must be a single shorthand
                    pointer to an element in the same XML file, i.e.
                    must look like <code>#duck</code>.</p>
                  </remarks>
                </attDef>
              </attList>
            </elementSpec>
            <!-- ... -->
            <dataSpec ident="xmpdata.shorthandPointer">
              <desc>defines the range of attribute values used to
              provide a single shorthand pointer URI</desc>
              <content>
                <dataRef name="anyURI" restriction="#\i\c*"/>
              </content>
              <remarks>
                <p>Formerly referred to as a
                <soCalled>barename</soCalled>
                identifier.</p></remarks>
            </dataSpec>
          </egXML>
          <p>Two snippets of ODD code which together use a facet to
          enforce that the value of <att>ref</att> of <gi>g</gi> is a
          shorthand pointer.</p>
          <p>While somewhat more complicated, this method has the
          advantage that it is both faster and generally easier on the
          encoder, in that the constraint will be expressed in RELAX
          NG (or the W3C Schema Language), and modern XML editors
          (like oXygen) can enforce such constraints immediately, as
          you type.</p>
        </figure>
        <!-- ========= figure eg04 ========= -->
        <figure xml:id="eg04">
          <head>Check that <att>ref</att> of <gi>g</gi> points to <emph>something</emph></head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="ref_of_g_points_to_local">
            <constraintSpec ident="g_points_locally" scheme="schematron">
              <desc>Example of how to test that the <att>ref</att> of <gi>g</gi>
              points to an element in the same document.</desc>
              <constraint>
                <sch:rule context="tei:g/@ref">
                  <sch:let name="ref" value="substring( normalize-space(.), 2 )"/>
                  <sch:assert test="id( $ref )">
                    The ref= of this 'g' element ("<sch:value-of select="."/>") does not point to an element in this document.
                  </sch:assert>
                </sch:rule>
              </constraint>
            </constraintSpec>
          </egXML>
          <p>This example presumes that the <att>ref</att> of
          <gi>g</gi> is a shorthand pointer. Thus this constraint
          should be used in conjunction with something like <ptr
          target="#eg02"/> or <ptr target="#eg03"/>.</p>
        </figure>
        <!-- ========= figure eg05 ========= -->
        <figure xml:id="eg05">
          <head>Check that <att>ref</att> of <gi>g</gi> points to a <gi>char</gi> or a <gi>glyph</gi></head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="ref_of_g_points_to_char_or_glyph">
            <constraintSpec ident="g_points_to_local_char_or_glyph" scheme="schematron">
              <desc>Example of how to test that the <att>ref</att> of
              <gi>g</gi> points to a local <gi>char</gi> or
              <gi>glyph</gi>.</desc>
              <constraint>
                <sch:rule context="tei:g/@ref">
                  <sch:let name="ref" value="substring( normalize-space(.), 2 )"/>
                  <sch:assert test="id( $ref )/self::tei:char | id( $ref )/self::tei:glyph">
                    The ref= of 'g' is supposed to point to a 'char' or 'glyph'; this one ("<sch:value-of select="."/>") points to a '<sch:value-of select="local-name( id( $ref ) )"/>'.
                  </sch:assert>
                </sch:rule>
              </constraint>
            </constraintSpec>
          </egXML>
          <p>This example presumes that the <att>ref</att> of
          <gi>g</gi> is a shorthand pointer that actually points to
          <emph>something</emph> in the current document. Thus this
          constraint should be used in conjunction with a constraint
          like <ptr target="#eg04"/> (which itself should be used in
          conjunction with a constraint like <ptr target="#eg02"/> or
          <ptr target="#eg03"/>).</p>
          <p>There are several other ways to express the XPath in the
          <att>test</att> of the <gi>sch:assert</gi>, of course. E.g.,
          many would consider <code>id( $ref )[ self::tei:char |
          self::tei:glyph ]</code> better because it is more compact.
          In either of these example XPaths the union operator
          (<code>|</code>, which may also be expressed
          <code>union</code>) could be replaced with <code>or</code>
          to indicate an OrExpr.</p>
          <p>Note: use <code>self::tei:char</code>, not
          <code>self::char</code> — the use of the namespace prefix in
          the XPaths within the Schematron is currently required.</p>
        </figure>
        <!-- ========= figure eg06 ========= -->
        <figure xml:id="eg06">
          <head>Require the <att>url</att> of <gi>moduleRef</gi> to refer to a particular file</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg06_egXML">
            <elementSpec ident="moduleRef" module="tagdocs" mode="change">
              <attList>
                <attDef ident="url" mode="change">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef key="teidata.enumerated"/>
                  </datatype>
                  <valList type="closed" mode="add">
                    <valItem ident="./kryptonian.rng">
                      <desc>Our special module for Kryptonian constructs</desc>
                    </valItem>
                  </valList>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>ODD fragment which requires that the value of
          <att>url</att> of <gi>moduleRef</gi> be
          <val>./kryptonian.rng</val>.</p>
        </figure>
        <!-- ========= figure eg07 ========= -->
        <figure xml:id="eg07">
          <head>Check that <att>url</att> of <gi>moduleRef</gi> refers to an RNG file in the same directory</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg07_egXML">
            <elementSpec ident="moduleRef" module="tagdocs" mode="change">
              <attList>
                <attDef ident="url" mode="change">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef name="anyURI" restriction="\./.+\.rng"/>
                  </datatype>
                  <remarks>
                    <p>The <att>url</att> attribute must refer to a file in the
                    same directory as the ODD customization file, and must have the
                    file extension <val>.rng</val>.</p>
                  </remarks>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
        </figure>
        <!-- ========= figure eg08 ========= -->
        <figure xml:id="eg08">
          <head>Ensure file is readable XML</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg08_egXML">
            <elementSpec ident="moduleRef" module="tagdocs" mode="change">
              <attList>
                <attDef ident="url" mode="change">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef name="anyURI" restriction="[^ #]+"/>
                  </datatype>
                  <constraintSpec scheme="schematron" ident="moduleRef_url_readable">
                    <constraint>
                      <sch:rule context="tei:moduleRef/@url">
                        <sch:assert test="doc-available( resolve-uri( ., base-uri(/) ) )">Module <sch:value-of select="."/> is not readable, well-formed XML.</sch:assert>
                      </sch:rule>
                    </constraint>
                  </constraintSpec>
                  <remarks>
                    <p>The <att>url</att> attribute must refer to a file, not
                    an element.</p>
                  </remarks>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>The constraints placed on the <att>url</att> attribute of
          <gi>moduleRef</gi> by the <att>restriction</att> attribute
          ensure both that the value is a single reference (by
          disallowing whitespace), and also is not a reference to a
          particular element within a file (by disallowing number sign
          (<q>#</q>, U+0023)).</p>
          <!--
              Do we need a paragraph explaining that testing for
              U+0020 is sufficient when dealing with xsd:anyURI? I am
              a bit hesitant to write one, because although I know it
              *does* work (I tested it), I do not know *why*. Likely
              internal whitespace is reduced to a single blank in an
              xsd:anyURI before comparision, but I do not know that; I
              am afraid this is an aspect of RELAX NG that escapes me.
              —Syd, 2021-12-25
          -->
          <p>The <gi>constraintSpec</gi> then tests that said file is
          readable, well-formed XML. The use of
          <code>resolve-uri()</code> assures that if a relative URI is
          used, the document requested is the one at that URI relative
          to the input document, not the Schematron schema.</p>
        </figure>
        <!-- ========= figure eg09 ========= -->
        <figure xml:id="eg09">
          <head>Ensure file is readable RELAX NG grammar</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg09_egXML">
            <elementSpec ident="moduleRef" module="tagdocs" mode="change">
              <attList>
                <attDef ident="url" mode="change">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef name="anyURI" restriction="[^ #]+"/>
                  </datatype>
                  <constraintSpec scheme="schematron" ident="moduleRef_url_relaxng">
                    <constraint>
                      <sch:rule context="tei:moduleRef/@url">
                        <sch:assert test="doc-available( resolve-uri( ., base-uri(/) ) )
                                          and
                                          doc( resolve-uri( ., base-uri(/) ) )/rng:grammar">
                          Module <sch:value-of select="."/> is not a readable,
                          well-formed RELAX NG grammar in the XML syntax.
                        </sch:assert>
                      </sch:rule>
                    </constraint>
                  </constraintSpec>
                  <remarks>
                    <p>The <att>url</att> attribute must refer to a
                    RELAX NG grammar in the XML syntax.</p>
                  </remarks>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>See comments for <ptr target="#eg08"/>.</p>
          <p>The prefix <code>rng:</code> is automatically bound to
          the RELAX NG namespace by the TEI ODD processor.</p>
          <p>Note that the test for <code>doc-available()</code> is
          not strictly necessary; the same pointers will succeed or
          fail with or without that test. But with the
          <code>doc-available()</code> test the user gets the custom
          error message when the file is not readable well-formed XML;
          without it, the user gets a generic error message, e.g.
          <q>No such file or directory</q>.</p>
        </figure>
        <!-- ========= figure eg10 ========= -->
        <figure xml:id="eg10">
          <head>Require <gi>persName</gi> to refer to a <gi>person</gi> in the local personography</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg10_egXML">
            <elementSpec ident="persName" module="namesdates" mode="change">
              <attList>
                <attDef ident="ref" mode="change">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef name="anyURI" restriction="(\./)?persons\.xml#\i\c*"/>
                  </datatype>
                  <constraintSpec scheme="schematron" ident="ref_of_persName_points_to_person">
                    <constraint>
                      <sch:rule context="tei:persName/@ref">
                        <sch:let name="file" value="substring-before( ., '#')"/>
                        <sch:let name="ID" value="substring-after( ., '#')"/>
                        <sch:let name="element_found" value="doc( $file )//id( $ID )"/>
                        <sch:assert test="$element_found[ self::tei:person ]">
                          persName should refer to a person; this one
                          refers to <sch:value-of select="
                            if ($element_found )
                            then concat( 'a ', local-name( $element_found ) )
                            else 'nothing'"/>.
                        </sch:assert>
                      </sch:rule>
                    </constraint>
                  </constraintSpec>
                  <remarks>
                    <p>The <att>ref</att> attribute must refer to a <gi>person</gi>
                    element in the persons.xml file.</p>
                  </remarks>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>This constraint takes advantage of Schematron’s facility
          for storing values in <soCalled>variables</soCalled>.</p>
        </figure>
        <!-- ========= figure eg11 ========= -->
        <figure xml:id="eg11">
          <head>Ensure <att>uri</att> of <gi>equiv</gi> is present and refers to an item on a particular page</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg11_egXML">
            <elementSpec ident="equiv" module="tagdocs" mode="change">
              <attList>
                <attDef ident="uri" mode="change" usage="req">
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef name="anyURI"
                             restriction="https?://www\.wwp(-test)?\.(northeastern|neu)\.edu/markup_taxonomy.xhtml#\i\c*"/>
                  </datatype>
                  <remarks>
                    <p>The <att>url</att> attribute is required, and
                    must refer to an ID on our markup_taxonomy
                    page.</p>
                  </remarks>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>Note that this constraint ensures that the <att>uri</att>
          attribute has the correct syntax, but does not actually try
          to retrieve the document it refers to.</p>
          <p>The <q>our markup_taxonomy</q> web page is accessible at
          eight different possible URLs; the <att>restriction</att>
          allows only these eight URLs.</p>
        </figure>
        <!-- ========= figure eg12 ========= -->
        <figure xml:id="eg12">
          <head>Require a <att>filter</att> on <gi>equiv</gi> that points to an XSLT program</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg12_egXML">
            <elementSpec ident="equiv" module="tagdocs" mode="change">
              <attList>
                <attDef ident="filter" mode="change" usage="req">
                  <constraintSpec scheme="schematron" ident="filter_is_XSLT">
                    <constraint>
                      <sch:rule context="tei:equiv/@filter">                  
                        <sch:ns prefix="xsl" uri="http://www.w3.org/1999/XSL/Transform"/>
                        <sch:let name="xsltNS" value="'http://www.w3.org/1999/XSL/Transform'"/>
                        <sch:let name="relURI" value="resolve-uri( normalize-space(.), base-uri(/) )"/>
                        <sch:assert test="doc($relURI)/*[namespace-uri() eq $xsltNS  or  @xsl:version]">
                          The filter must be an XSLT program!
                        </sch:assert>
                      </sch:rule>
                    </constraint>
                  </constraintSpec>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>Note that this constraint actually tries to retrieve the
          document <att>filter</att> refers to. This results in more
          thorough testing than <ptr target="#eg11"/>, but requires a
          working internet connection or re-direction via an <ref
          target="https://xmlcatalogs.org/catalogs-1.1.html">XML
          Catalog</ref>.</p>
          <p>The use of <code>resolve-uri()</code> assures that if a
          relative URI is used, the document requested is the one at
          that URI relative to the input document, not the Schematron
          schema.</p>
          <p>Note that the TEI processors do not automatically bind
          the <code>xsl:</code> prefix to the XSLT namespace. It is
          common to put all of your <gi>sch:ns</gi> elements in a
          <gi>constraintSpec</gi> that is a direct child of the
          <gi>schemaSpec</gi> of your customization ODD.</p>
        </figure>
        <!-- ========= figure eg13 ========= -->
        <figure xml:id="eg13">
          <head><att>rendition</att> points to 1 or 2 <gi>rendition</gi>s</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg13_egXML">
            <classSpec ident="att.global.rendition" mode="change" type="atts">
              <attList>
                <attDef ident="rendition" mode="change">
                  <datatype minOccurs="1" maxOccurs="2">
                    <dataRef key="teidata.pointer"/>
                  </datatype>
                  <constraintSpec scheme="schematron" ident="rendition_ponts_to_rendition">
                    <desc>Ensure that each pointer in a
                      <att>rendition</att> points to a
                      <gi>rendition</gi>.</desc>
                    <constraint>
                      <sch:rule context="@rendition">

                        <!-- Get sequence of 1 or 2 pointers -->
                        <sch:let name="RENDITIONs" value="tokenize( normalize-space(.),'&#x20;')"/>
                        
                        <!-- Test that each starts with '#' -->
                        <sch:assert test="starts-with( $RENDITIONs[1], '#')">
                          First rendition reference does not start with a number sign.
                        </sch:assert>
                        <sch:assert test="empty( $RENDITIONs[2] )  or  starts-with( $RENDITIONs[2], '#')">
                          Second rendition reference does not start with a number sign.
                        </sch:assert>
                        
                        <!-- Test that they point to <rendition>s -->
                        <!--
                            Note: since leaving off the '#' is BY FAR
                            the most common error, we actually test the
                            two tokens that should start with '#'
                            whether or not they do (adding the '#' if
                            needed), thus avoiding two error messages
                            for "style03" when "#style03" would have
                            worked.
                        -->
                        <sch:let name="RND_ONE" value="replace( $RENDITIONs[1], '^#','')"/>
                        <sch:let name="RND_TWO" value="replace( $RENDITIONs[2], '^#','')"/>
                        <sch:assert test="id( $RND_ONE )/self::tei:rendition">
                          The first rendition reference (<sch:value-of
                          select="$RENDITIONs[1]"/>) does not point to a
                          local 'rendition' element.
                        </sch:assert>
                        <sch:assert test="empty( $RND_TWO )  or  id( $RND_TWO )/self::tei:rendition">
                          The second rendition reference (<sch:value-of
                          select="$RENDITIONs[2]"/>) does not point to a
                          local 'rendition' element.
                        </sch:assert>
                      </sch:rule>
                    </constraint>
                  </constraintSpec>
                </attDef>
              </attList>
              <remarks>
                <p>Here at the <title>The Papers of Dr. Virgil
                Swann</title> project the <att>rendition</att> attribute
                is limited to 1 or 2 values. If a more complex description
                of rendition is required, the individual CSS declarations
                (property:value pairs) should be combined into a new
                <gi>rendition</gi>, and the <att>rendition</att> should
                point at that.</p>
                <p>This constraint on <att>rendition</att> is global,
                effecting <emph>all</emph> such attributes, whether
                defined is this class or not. However, since we do not
                declare any other <att>rendition</att> attributes,
                this is not a concern.</p>
              </remarks>
            </classSpec>
          </egXML>
          <p>Note that the comparison <code>$VAR eq ''</code> could
          have been used instead of the function <code>empty( $VAR
          )</code>.</p>
        </figure>
        <!-- ========= figure eg14 ========= -->
        <figure xml:id="eg14">
          <head>Check that each pointer in <att>wit</att> points to <gi>witness</gi></head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg14_egXML">
            <elementSpec ident="rdg" module="textcrit" mode="change">
              <attList>
                <attDef ident="wit" mode="change">
                  <constraintSpec scheme="schematron" ident="wit_points_to_witnesses">
                    <constraint>
                      <sch:rule context="tei:rdg/@wit">

                        <!-- value is a string (supposedly of shorthand pointers); change it to a sequence -->
                        <sch:let name="WITs" value="tokenize( normalize-space(.), '&#x20;')"/>
                        
                        <!-- check each starts with number sign -->
                        <sch:assert test="every $WIT in $WITs satisfies starts-with( $WIT, '#')">
                          One (or more) of the pointers in this @wit attribute does (do) not start with a '#'.
                        </sch:assert>
                        
                        <!--
                            develop sequence of ID values (i.e., IDREFs), rather than shorthand pointers,
                            by stripping off '#', if present, from each
                        -->
                        <sch:let name="WITIDs" value="for $WIT in $WITs return replace( $WIT, '^#','')"/>
                        <!--
                            Note that we actually test the pointer whether it had
                            a '#' or not, thus avoiding two error messages for
                            "wit015" when "#wit015" would have worked.
                        -->
                        
                        <!-- Test that each points to a <witness>, storing result as boolean -->
                        <sch:let name="points_to_witness"
                                 value="for $ID in $WITIDs return exists( id( $ID )[self::tei:witness] )"/>
                        
                        <!--
                            Now we have a sequence of boolean values. If
                            even one is false(), we have a problem that
                            needs to be reported to the user.
                        -->
                        <sch:report test="$points_to_witness = false()">
                          One (or more) of the pointers of this @wit does (do) not point
                          to a 'witness' element. The pointers of this @wit point to the
                          following items, in the order specified:
                          <sch:value-of select="for $ID in $WITIDs return
                                                  if ( exists( id( $ID ) ) )
                                                    then local-name( id( $ID ) )
                                                    else '*nothing*'"/>.
                        </sch:report>
                        
                      </sch:rule>
                    </constraint>
                  </constraintSpec>
                  <remarks>
                    <p>Note that the <att>wit</att> is constrained here, not in the definition
                    of <name>att.witnessed</name>, because the only other element that is a member
                    of <name>att.witnessed</name> is <gi>lem</gi>, and at our project <gi>lem</gi>
                    never uses the <att>wit</att> attribute.</p>
                  </remarks>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
        </figure>
        <!-- ========= figure eg15 ========= -->
        <figure xml:id="eg15">
          <head>Required <att>ref</att> of <gi>persName</gi> eventually refers to <gi>person</gi></head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg15_egXML">
            <elementSpec ident="persName" module="namesdates" mode="change">
              <constraintSpec scheme="schematron" ident="people_have_refs">
                <constraint>
                  <sch:rule context="tei:persName[ not( ancestor::tei:teiHeader ) ]">
                    <sch:assert test="@ref">A transcribed personal name (as opposed to a personal name in the metadata) must have a @ref</sch:assert>
                  </sch:rule>
                </constraint>
              </constraintSpec>
              <constraintSpec scheme="schematron" ident="persName_refers_eventually_to_person">
                <constraint>
                  <sch:rule context="tei:persName/@ref">
                    <sch:let name="direct_target" value="id( substring-after( normalize-space(.), '#') )"/>
                    <sch:let name="indirect_targets" value="
                        if ( $direct_target/self::tei:person )
                        then $direct_target
                        else
                          if ( $direct_target[ self::tei:link | self::tei:alt ][ @type eq 'person'] )
                          then for $it in tokenize( normalize-space( $direct_target/@target ) ) return id( substring-after( $it, '#') )
                          else parent::* "/>
                    <sch:let name="targs_pt_to_person" value="for $targ in $indirect_targets return exists( $targ/self::tei:person )"/>
                    <sch:report test="$targs_pt_to_person = false()">
                      At least one of the pointers in "<sch:value-of select="normalize-space(.)"/>" does not end
                      up pointing to a &lt;person>, even via a &lt;link type="person"> or an &lt;alt type="person">.
                    </sch:report>
                  </sch:rule>
                </constraint>
              </constraintSpec>
            </elementSpec>
          </egXML>
          <p>This set of constraints requires that <gi>persName</gi>
          have a <att>ref</att> unless it is part of the document
          metadata, and also requires that the pointers on the
          <att>ref</att> attribute point to <gi>person</gi> elements,
          even if through intermediate <gi>alt</gi> or
          <gi>link</gi>.</p>
        </figure>
        <!-- ========= figure eg16 ========= -->
        <figure xml:id="eg16">
          <head>Required <att>ref</att> of <gi>persName</gi>
          eventually refers to <gi>person</gi> using abstract
          patterns</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg16_egXML">
            <constraintSpec ident="abstract_indirect_person" scheme="schematron" >
              <constraint>
                <sch:pattern abstract="true" id="abstract_indirect_person">
                  <sch:rule context="$alt_or_link_of_type_person">
                    <sch:let name="TARGETs" value="tokenize( normalize-space( @target ), '&#x20;')"/>
                    <sch:let name="IDREFs" value="for $target in $TARGETs return replace( $target, '^#','')"/>
                    <sch:let name="TARGET_ELEMENTs"
                             value="for $idref in $IDREFs return
                                      (: if it points to an element in this document … :)
                                      if ( id( $idref ) )
                                        (: … then return the element it points to … :)
                                        then id( $idref )
                                        (: … otherwise return current link or alt element, which is not a
                                           person element, so will be caught as an error, below. :)
                                        else ."/><!-- myself as target is an error either way :-) -->
                    <sch:let name="TEs_ARE_PERSON"
                             value="for $element in $TARGET_ELEMENTs return
                                      exists( $element/self::tei:person )"/>
                    <sch:report test="$TEs_ARE_PERSON = false()">One or more of the items pointed to by
                    @target of this <sch:value-of select="local-name(.)"/> (of type "person") does not
                    point to a 'person' element.</sch:report>
                  </sch:rule>
                </sch:pattern>
              </constraint>
            </constraintSpec>
            <classSpec ident="att.canonical" type="atts" mode="change">
              <attList>
                <attDef ident="key" mode="delete"/>
                <attDef ident="ref" mode="change">
                  <desc>provides a definition for the entity being named
                  or referred to via a single URI pointer</desc>
                  <datatype minOccurs="1" maxOccurs="1">
                    <dataRef key="teidata.pointer"/>
                  </datatype>
                  <remarks>
                    <p>If the word or phrase being encoded directly
                    refers to multiple entities, this attribute should
                    point to a <gi>link</gi> which in turn should
                    point to the definitions.</p>
                    <p>If the word or phrase being encoded is
                    ambiguous, this attribute should point to a
                    <gi>alt</gi> which in turn should point to the
                    various possible definitions.</p>
                  </remarks>
                </attDef>
              </attList>
            </classSpec>
            <elementSpec ident="alt" mode="change">
              <constraintSpec scheme="schematron" ident="indirect_person_alt">
                <constraint>
                  <sch:pattern id="concrete_indirect_person_alt" is-a="abstract_indirect_person">
                    <sch:param name="alt_or_link_of_type_person"
                               value="tei:alt[ ( @type, ../@type ) = 'person' ]"/>
                  </sch:pattern>
                </constraint>
              </constraintSpec>
            </elementSpec>
            <elementSpec ident="link" mode="change">
              <constraintSpec scheme="schematron" ident="indirect_person_link">
                <constraint>
                  <sch:pattern id="concrete_indirect_person_link" is-a="abstract_indirect_person">
                    <sch:param name="alt_or_link_of_type_person"
                               value="tei:link[ ( @type, ../@type ) = 'person' ]"/>
                  </sch:pattern>
                </constraint>
              </constraintSpec>
            </elementSpec>
            <elementSpec ident="persName" mode="change">
              <constraintSpec scheme="schematron" ident="people_have_refs">
                <constraint>
                  <sch:rule context="tei:persName[ not( ancestor::tei:teiHeader ) ]">
                    <sch:assert test="@ref">A transcribed personal
                    name (as opposed to a personal name in the
                    metadata) must have a @ref</sch:assert>
                  </sch:rule>
                </constraint>
              </constraintSpec>
              <attList>
                <attDef mode="change" ident="ref">
                  <constraintSpec scheme="schematron" ident="persName_refers_to_person_alt_or_link">
                    <constraint>
                      <sch:rule context="tei:persName/@ref">
                        <sch:let name="target" value="id( substring-after( normalize-space(.), '#') )"/>
                        <sch:assert test="  $target/self::tei:person
                                          | $target/self::tei:alt[ ( @type, ../@type) = 'person']
                                          | $target/self::tei:link[ ( @type, ../@type ) = 'person']
                                          ">The @ref of a personal name should refer to a 'person' element,
                        or either a 'link' or 'alt' element with type="person"; but this one points to the
                        '<sch:value-of select="name($target)"/>' with ID
                        "<sch:value-of select="$target/@xml:id"/>".</sch:assert>
                      </sch:rule>
                    </constraint>
                  </constraintSpec>
                </attDef>
              </attList>
            </elementSpec>
          </egXML>
          <p>This set of constraints makes use of the abstract pattern
          facility of ISO Schematron.</p>
        </figure>
        <!-- ========= figure eg17 ========= -->
        <figure xml:id="eg17">
          <head>Resolve <gi>prefixDef</gi> for references to people</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg17_egXML">
            <sch:rule context="tei:persName/@ref | tei:rs[@type eq 'person']/@ref | tei:author/@ref">

              <!-- remember the element type and attribute value we matched on -->
              <sch:let name="gi" value="local-name(..)"/>
              <sch:let name="val" value="normalize-space(.)"/>
              
              <!-- get a sequence of the references in the attr value -->
              <sch:let name="REFs" value="tokenize( $val )"/>

              <!-- get a sequence of the prefixes thereof[1] -->
              <sch:let name="PREFs" value="for $r in $REFs return substring-before( $r,':')"/>

              <!-- get a sequence of the URLs associated with each prefix[1] -->
              <sch:let name="URLs" value="for $prefix in $PREFs return
                                            substring-before(
                                              /*/tei:teiHeader
                                              /tei:encodingDesc/tei:listPrefixDef
                                              //tei:prefixDef[ @ident eq $prefix ]
                                              /@replacementPattern,
                                              '#'
                                            )" />

              <!-- get a sequence of the keys associated with each prefix[1] -->
              <sch:let name="KEYs" value="for $ref in $REFs return substring-after( $ref,':')"/>
              
              <!-- get a sequence of the elements pointed to by each key in the corresponding URL. -->
              <!--
                  If, for some reason, the URL & KEY combination does not
                  point to a TEI element, record the current node as the
                  target. (Thus we can test later to see if the pointer failed
                  by testing for the current node.)
              -->
              <sch:let name="PERSONs"
                       value="for $key in $KEYs return
                              if ( count( index-of( $KEYs, $key ) ) eq 1 )
                              then 
                                if ( doc-available( $URLs[ index-of( $KEYs, $key )] ) )
                                then
                                  if ( document( $URLs[ index-of( $KEYs, $key )] )//tei:*[ @xml:id eq $key ] )
                                  then
                                    document( $URLs[ index-of( $KEYs, $key )] )//tei:*[ @xml:id eq $key ]
                                  else .
                                else .
                              else ."/>

              <!-- If there are no references in @ref, that's an error; report it -->
              <sch:report test="count( $REFs ) eq 0">
                Empty @ref of element '<sch:value-of select="$gi"/>'
              </sch:report>

              <!-- If there are any references that point to something
                   other than <tei:person>, report it (them).  -->
              <sch:report test="count( $PERSONs[ not( self::tei:person ) ] ) gt 0">
                Each pointer in the @ref attribute of a '<sch:value-of
                select="$gi"/>' element should point to a 'person'
                element, but the pointers in this one ("<sch:value-of
                select="$val"/>") point to the following in the order
                specified:
                <sch:value-of
                    select="if ( not( $PERSONs ) )
                            then 'nowhere'
                            else for $p in $PERSONs return
                              if ( $p is . )
                              then ' *nothing*'
                              else concat(' &amp;lt;', name($p), '>')"/>.
              </sch:report>
              <!-- 
               [1] The PREFs, URLs, KEYs, and PERSONs sequences have the
                   same number of items as the REFs sequence, but some of the
                   items may be nil.
              -->
            </sch:rule>
          </egXML>
          <p>The constraint above checks only <att>ref</att>
          attributes that occur on <gi>persName</gi>, <gi>author</gi>,
          and <tag>rs type="person"</tag>.</p>
          <p>Note that the constraints above only check the
          <gi>prefixDef</gi>s that are in the <gi>teiHeader</gi> child
          of the outermost element; <gi>prefixDef</gi>s inside nested
          <gi>TEI</gi> or <gi>teiCorpus</gi> elements are ignored.
          This can easily be changed by either removing just the
          asterisk (<q>*</q>, U+002A) from the XPath, or replacing the
          entire precise XPath with <code>//tei:prefixDef</code>.
          These paths may be slower, however.</p>
          <p>Earlier examples have avoided use of the less-than sign
          (<q>&lt;</q>, U+003C) and greater-than sign (<q>></q>,
          U+003E) inside messages to indicate <q>this is an
          element</q>. The only reason for this is that some
          Schematron processors produce ugly output when these
          characters occur in a message.</p>
        </figure>
        <!-- ========= figure eg18 ========= -->
        <figure xml:id="eg18">
          <head>XIncluded yet? — one size fits all</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg18_egXML">
            <sch:ns prefix="xi" uri="http://www.w3.org/2001/XInclude"/>
            <sch:rule context="/">
              <sch:report test="//xi:*">
                Error! XInclude processing has not been performed yet.
                Many rule-based tests will fail because of this.
              </sch:report>
            </sch:rule>
          </egXML>
          <p>This is a very generic test which is easy to write and
          very fast, but does not tell the user which particular test
          will fail.</p>
          <p>Note that the TEI processors do not automatically bind
          the <code>xi:</code> prefix to the XInclude namespace. It is
          common to put all of your <gi>sch:ns</gi> elements in a
          <gi>constraintSpec</gi> that is a direct child of the
          <gi>schemaSpec</gi> of your customization ODD.</p>
        </figure>
        <!-- ========= figure eg19 ========= -->
        <figure xml:id="eg19">
          <head>XIncluded yet? — per-test reporting</head>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg19_egXML">
            <sch:rule context="tei:textClass[ not( preceding::tei:textClass ) ]">
              <!-- The predicate above simply keeps this error to a
                   single occurrence, instead of once for each
                   <textClass>. The TEI permits multiple <textClass>
                   elements in any given <profileDesc> (and multiple
                   <profileDesc> elements in any given <teiHeader>);
                   but most projects only use one, so you may well not
                   need this predicate. -->
              <sch:assert test="/tei:TEI/tei:teiHeader//tei:taxonomy">
                There is no &amp;lt;taxonomy> in the header, so no &amp;lt;catRef> pointer can point into the taxonomy.
                (Have you performed XInclude processing yet?)
              </sch:assert>
            </sch:rule>
            <sch:rule context="tei:catRef[ /tei:TEI/tei:teiHeader//tei:taxonomy ]">
              <sch:let name="target" value="substring-after( normalize-space( @target ),'#')"/>
              <sch:assert test="id($target)/self::tei:category">
                This &amp;lt;catRef> does not point to a &amp;lt;category>!
              </sch:assert>
            </sch:rule>
          </egXML>
          <p>This example presumes that the <att>target</att> of
          <gi>catRef</gi> is a single shorthand pointer that actually
          points to <emph>something</emph> in the current document.
          Thus this constraint should be used in conjunction with
          constraints like those in <ptr target="#eg01"/> and <ptr
          target="#eg04"/> (which itself should be used in conjunction
          with a constraint like <ptr target="#eg02"/> or <ptr
          target="#eg03"/>).</p>
        </figure>
        <!-- ... figures 20 to 57 go here, if ever needed ... -->
        <!-- ========= figure eg58 ========= -->
        <figure xml:id="eg58">
          <head>Test for <emph>any</emph> local URI</head>
          <p>This example is different from the others in that it is
          not intended to be copied, pasted, modified, and used. (And
          thus has not been tested as well as the others.) It is
          really here only to demonstrate that a) testing for the
          syntax of a general URI <emph>can</emph> be done, and b) it
          is very complex. I suspect that the vast majority of TEI
          projects use only a very specific subset of local URI
          formats — e.g., only relative filepaths with or without a
          fragment identifier (e.g. <val>KRdict.xml#e05</val> or just
          <val>KRdict.xml</val>), or even only relative filepaths that
          start with an explicit dot-segment (e.g.
          <val>./KRdict.xml</val>) — and thus would either test only
          for the syntax of that particular format, or simply test to
          see if the desired target is retrievable, which is
          demonstrated in <ptr target="#eg07"/>.</p>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg58_egXML">
            <sch:rule context="tei:ptr[@type='localOnly']/@target">

              <!-- ******** First, define the component pieces for RFC 3986 “relative-ref” ******** -->

              <sch:let name="HEXDIG"      value="'[0-9A-Fa-f]'"/>
              <sch:let name="pct-encoded" value="concat('%', $HEXDIG, $HEXDIG )"/>
              <!--
                  Since “unrserved” and “sub-delims” are never used independently
                  we simply use a combined form here:
                  ursdsh = unreserved and sub-delims sans hyphen
              -->
              <sch:let name="unressubdel" value='"A-Za-z0-9\-._~!$&amp;&apos;()*+,;="'/>
              <!--
                  “unencoded” is a combination of “unreserved”, “sub-delims”, COMMERCIAL AT, and COLON;
                  “unencodednc” is the same without COLON.
              -->
              <sch:let name="unencoded"   value="concat('[', $unressubdel, '@', ':', ']')"/>
              <sch:let name="unencodednc" value="concat('[', $unressubdel, '@',      ']')"/>
              
              <sch:let name="pchar"   value="concat( $unencoded,   '|', $pct-encoded )"/>
              <sch:let name="ncpchar" value="concat( $unencodednc, '|', $pct-encoded )"/>
              
              <sch:let name="segment"       value="concat('(', $pchar,   ')*')"/>
              <sch:let name="segment-nz"    value="concat('(', $pchar,   ')+')"/>
              <sch:let name="segment-nz-nc" value="concat('(', $ncpchar, ')+')"/>
              
              <sch:let name="path-abempty"  value="concat('(/', $segment, ')*')"/>
              <sch:let name="path-absolute" value="concat('/(', $segment-nz, '(/', $segment, ')*)?')"/>
              <sch:let name="path-noscheme" value="concat( $segment-nz-nc, '(/', $segment, ')*' )"/>
              <sch:let name="path-empty"    value="'[empty]{0}'"/>
              
              <sch:let name="query"    value="concat('(', $pchar, '|/|\?)*')"/>
              <sch:let name="fragment" value="concat('(', $pchar, '|/|\?)*')"/>

              <!-- The following are only used in the “host” pattern -->
              <sch:let name="dec-octet"
                       value="'((1?[1-9])?[0-9]|2([0-4][0-9]|5[0-5]))'"/>
              <sch:let name="IPv4address"
                       value="concat( $dec-octet, '.', $dec-octet, '.', $dec-octet, '.', $dec-octet )"/>
              <sch:let name="h16" value="'[0-9A-Fa-f]{1,4}'"/>
              <sch:let name="h16c" value="concat('(', $h16, ':',')')"/>
              <sch:let name="ls32" value="concat('((', $h16, ':', $h16, ')|(', $IPv4address, '))')"/>
              <sch:let name="IPv6addr_a" value="concat(                                   $h16c, '{6}', $ls32 )"/>
              <sch:let name="IPv6addr_b" value="concat(                             '::', $h16c, '{5}', $ls32 )"/>
              <sch:let name="IPv6addr_c" value="concat('(', $h16c, '{0,0}', $h16, ')?::', $h16c, '{4}', $ls32 )"/>
              <sch:let name="IPv6addr_d" value="concat('(', $h16c, '{0,1}', $h16, ')?::', $h16c, '{3}', $ls32 )"/>
              <sch:let name="IPv6addr_e" value="concat('(', $h16c, '{0,2}', $h16, ')?::', $h16c, '{2}', $ls32 )"/>
              <sch:let name="IPv6addr_f" value="concat('(', $h16c, '{0,3}', $h16, ')?::', $h16c, '{1}', $ls32 )"/>
              <sch:let name="IPv6addr_g" value="concat('(', $h16c, '{0,4}', $h16, ')?::',               $ls32 )"/>
              <sch:let name="IPv6addr_h" value="concat('(', $h16c, '{0,5}', $h16, ')?::',               $ls32 )"/>
              <sch:let name="IPv6addr_i" value="concat('(', $h16c, '{0,6}', $h16, ')?::',               $ls32 )"/>
              <sch:let name="IPv6address"
                       value="concat('(',
                                     '|', $IPv6addr_a,
                                     '|', $IPv6addr_b,
                                     '|', $IPv6addr_c,
                                     '|', $IPv6addr_d,
                                     '|', $IPv6addr_e,
                                     '|', $IPv6addr_f,
                                     '|', $IPv6addr_g,
                                     '|', $IPv6addr_h,
                                     '|', $IPv6addr_i,
                                     ')')"/>
              <sch:let name="IPvFuture" value="concat('v', $HEXDIG, '+\.[', $unressubdel, ':', ']*')"/>
              <sch:let name="IP-literal"
                       value="concat('\[(', $IPv6address, '|', $IPvFuture, ')\]')"/>
              <sch:let name="reg-name" value="concat('([', $unressubdel, ']|', $pct-encoded, ')*')"/>
              <!-- end “host”-pattern only portion -->
              
              <sch:let name="userinfo" value="concat('([', $unressubdel, ':]|', $pct-encoded, ')*')"/>
              <sch:let name="host" value="concat('(', $IP-literal, '|', $IPv4address, '|', $reg-name, ')')"/>
              <sch:let name="port" value="'[0-9]*'"/>
              <sch:let name="authority"
                       value="concat('(', $userinfo, '@)?', $host, '(:', $port, ')?')"/>

              <!-- ******** relative reference itself ******** -->
              <sch:let name="relative-ref"
                       value="concat('(', '//', $authority, $path-abempty,
                                     '|', $path-absolute,
                                     '|', $path-noscheme,
                                     '|', $path-empty,
                                     ')(\?', $query, ')?(#', $fragment, ')?'
                                     )"/>

              <!-- ******** now components of an RFC 8089 “file-URI” ******** -->
              <!-- “host” and “path-absolute” have already been defined, above. -->
              <sch:let name="file-auth" value="concat('(', $host, '|(localhost))')"/>
              <sch:let name="auth-path" value="concat( $file-auth, '?', $path-absolute )"/>
              <sch:let name="file-hier-part" value="concat( '(//', $auth-path, '|', $path-absolute, ')')"/>
              
              <!-- ******** the file-URI itself ********  -->
              <sch:let name="file-URI" value="concat('file:', $file-hier-part )"/>
              
              <!-- ******** now perform the actual test ******** -->
              <sch:assert test="matches(
                                  normalize-space(.),
                                  concat('^(', $relative-ref, '|', $file-URI, ')$')
                                )">
                Value of @target should be a local URI (a relative URI reference or a 'file:' scheme URI).
              </sch:assert>
            </sch:rule>
          </egXML>
          <p>Note that this constraint only tests that the value of
          <att>target</att> is either a relative URI reference (e.g.,
          <val>kryptonian_charDecl.xml</val>) or uses the
          <code>file:</code> URI scheme. It does not test that the
          file referred to with the <code>file:</code> scheme, if any,
          is actually local. This would require testing that the host
          specified is, in fact, the local machine. If the host is not
          specified or is <code>localhost</code>, it definitionally
          refers to the local machine. But for any other value of
          <code>host</code> the address would need to be resolved, the
          local address would need to be ascertained, and the two
          would need to be compared.</p>
          <p>Note that the test in this example does
          not permit zone identifiers, which were added to IPv6
          addresses by RFC 6874.</p>
        </figure>
        <!-- ========= figure eg59 ========= -->
        <figure xml:id="eg59">
          <head><att>ref</att> uses defined prefix</head>
          <p>This example tests that each separate pointer in a
          <att>ref</att> attribute on a <gi>geogName</gi>,
          <gi>name</gi>, <gi>persName</gi>, <gi>placeName</gi>,
          <gi>pubPlace</gi>, <gi>rs</gi>, or <gi>title</gi> element
          begins with a prefix that is defined in the
          <gi>listPrefixDef</gi>.</p>
          <egXML xmlns="http://www.tei-c.org/ns/Examples" xml:id="eg59_egXML">
            <sch:rule context=" tei:geogName[@ref]
                               |tei:name[@ref]
                               |tei:persName[@ref]
                               |tei:placeName[@ref]
                               |tei:pubPlace[@ref]
                               |tei:rs[@ref]
                               |tei:title[@ref]  ">
              <!-- get a sequence of the references in the attr value -->
              <sch:let name="REFs" value="tokenize( @ref )"/>
              <!-- get a sequence of the prefixes thereof[1] -->
              <sch:let name="PREFs"
                       value="for $r in $REFs return substring-before( $r,':')"/>
              <!-- get the list of defined prefixes -->
              <sch:let name="DEFINEDs"
                       value="for $prefixDef in /tei:TEI/tei:teiHeader/tei:encodingDesc/tei:listPrefixDef//tei:prefixDef
                              return $prefixDef/@ident"/>
              <!-- create a sequence of booleans that indicate if prefix is defined -->
              <sch:let name="BOOLEANs"
                       value="for $pfd in distinct-values( $PREFs ) return $pfd = $DEFINEDs"/>
              <!-- if any 1 of PREFs is not in list of DEFINEDs, warn user -->
              <sch:report test="$BOOLEANs = false()">
                One or more of the references in the @ref of this <sch:name/> does
                not start with a defined prefix.
                The complete set of pointers on this @ref is "<sch:value-of
                  select="normalize-space( @ref )"/>".
                The complete set of defined prefixes is <sch:value-of
                  select="string-join( $DEFINEDs, ', ')"/>.
              </sch:report>
            </sch:rule>
          </egXML>
          <p>Note that the above code requires an XSLT3
          <att>queryBinding</att>, as it uses <code>tokenize()</code>.
          In order to convert this to a constraint that will work with
          an XSLT2 <att>queryBinding</att>, change the <code>tokenize()</code> to <code>tokenize( normalize-space(.), '&#x20;')</code>.</p>
          <p>This constraint could be quite a bit simpler if there
          were never a need to test more than 1 value in a given
          <att>ref</att>. In which case, one would only need test that
          <code>substring-before( normalize-space( @ref ),':') =
          /tei:TEI/tei:teiHeader/tei:encodingDesc/tei:listPrefixDef//tei:prefixDef/@ident</code>.</p>
        </figure>
      </div>
    </back>
  </text>
</TEI>
