Re: can pure ODD define an alternation of attribute patterns?

Syd Bauman <[email protected]> Thu, 23 Mar 2017 13:42:12 -0400
Newsgroups gmane.text.tei.general
Message-ID <[email protected]>
I tihnk the short answer is "no", Pure ODD can't do that. But to be
honest, I haven't tried. This is in part because I know the `roma`
code that handles <attList org="choice"> is broken. (See bug 144.[1])

But ODD can do this, albeit in a roundabout, hack kind of way. (And,
it turns out in writing my test, I had to add a hack to the hack to
get around what I presume is a bug in ODD processing.)

The attached ODD is an example of a method that at best is likely to
be considered controversial, at worst bad practice. But it does
exactly what (I think) you want. It re-names the element <hi> to be
<methodOne>, and gives it three new required
attributes:
  attribute.1 = "pointer" | "character" | "byte_offset" | "time_in_sec"
  attribute.2 =  anyURI       nonNegInt     nonNegInt       int
  attribute.3 =  anyURI       nonNegInt     nonNegInt       int

A few caveats, in no particular order:

 * The method used will give some people angina.

 * Because RELAX NG cosntructs are used directly, you do not have the
   advantage that the value of attribute.3 is defined as
   teidata.pointer when attribute.1 is "pointer"; rather, it is
   defined directly as anyURI.

 * Hack: I had to use <rng:interleave> to group the attribute
   definitions; for some reason when I tried <rng:group> (which is
   what one would naturally expect to use) `roma` converted it to an
   <rng:choice>.

 * I really doubt you can get usable XSD, and you certainly can get
   helpful DTD, out of this.

So personally, I prefer the solution you recommended: just create a
closed list for @attribute.1, and then a small Schematron rule to
ensure that @attribute.2 and @attribute.3 are of the right datatype.

I have thrown that into the ODD as well, renaming <emph> to be
<methodTwo>, and giving it three new required attributes named
"anotherAtt.1" etc.

Notes
-----
[1] https://github.com/TEIC/Stylesheets/issues/144


> I would like to define an alternation of patterns of up to three 
> attributes, where the datatype of attribute.2 and attribute.3 is uniform 
> and depends on the value of attribute.1.
> 
> Something like the following (in **pseudo-markup**):
> 
> <rng:choice>
>     <rng:group>  "pointer", data.pointer,                      
> data.pointer </rng:group>
>     <rng:group>  "character",   data.nonNegativeInteger, 
> data.nonNegativeInteger </rng:group>
>     <rng:group>  "byte_offset", data.nonNegativeInteger, 
> data.nonNegativeInteger </rng:group>
>     <rng:group>  "time_in_sec", data.integer,                    
> data.integer </rng:group>
>    .... etc.
> </rng:choice>
> 
> I could do that in RNG, but can I do that in ODD as well?
> 
> If I understand correctly, ODD expects me to list the three attDef 
> declarations in an <attList> for my new class, and to declare a new data 
> type that groups the data types that I need for attribute.2 and 
> attribute.3, and to make the value of attribute.1 a closed list, and 
> then to slam a huuuge piece of Schematron onto this (hopefully inside 
> this very class definition; I haven't checked that) that would attempt 
> to rule out unwanted sequences.
> 
> Is my diagnosis correct? I probably wouldn't mind being wrong.
> 
> I also admit to having a rather hazy idea of the extent of the 
> difference in "staticness" between listing potential patterns in an RNG 
> schema on the one hand, and defining them in the ODD on the other. I 
> mean, I am not really sure if I could <alternate> a series of 
> <sequence>s containing <attDef>s. Would I end up defining a single 
> attribute several times? Would/Should ODD allow me to do that?
T4PB.odd (text/plain, 5.8 KB)
<?xml version="1.0" encoding="UTF-8"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0" xml:lang="en"
    xmlns:sch="http://purl.oclc.org/dsdl/schematron"
    xmlns:rng="http://relaxng.org/ns/structure/1.0"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema-datatypes">
  <teiHeader>
    <fileDesc>
      <titleStmt>
        <title>Test for Piotr Banski</title>
        <author xml:id="sb" ref="p:sbauman.emt">Syd Bauman</author>
      </titleStmt>
      <publicationStmt>
        <p>Unpublished, just to be posted to TEI-L</p>
        <availability>
          <p>© 2017 Syd Bauman and Northeastern University Women
          Writer's Project. Available via CC 4.0 BY-SA.</p>
        </availability>
      </publicationStmt>
      <sourceDesc>
        <p>No source, this ODD document is the original.</p>
      </sourceDesc>
    </fileDesc>
  </teiHeader>
  <text>
    <body>
      <schemaSpec ident="T4PB" prefix="T4PB_" start="TEI div p">
	<moduleRef key="tei"/>
	<moduleRef key="core"/>
	<moduleRef key="header"/>
	<moduleRef key="textstructure" except="div1 div2 div3 div4 div5 div6 div7"/>
	<macroSpec ident="att1ptr" mode="add">
	  <content>
	    <rng:interleave>
	      <rng:attribute name="attribute.1">
		<rng:value type="token">pointer</rng:value>
	      </rng:attribute>
	      <rng:attribute name="attribute.2">
		<rng:data type="anyURI"/>
	      </rng:attribute>
	      <rng:attribute name="attribute.3">
		<rng:data type="anyURI"/>
	      </rng:attribute>
	    </rng:interleave>
	  </content>
	</macroSpec>
	<macroSpec ident="att1char" mode="add">
	  <content>
	    <rng:interleave>
	      <rng:attribute name="attribute.1">
		<rng:value type="token">character</rng:value>
	      </rng:attribute>
	      <rng:attribute name="attribute.2">
		<rng:data type="nonNegativeInteger"/>
	      </rng:attribute>
	      <rng:attribute name="attribute.3">
		<rng:data type="nonNegativeInteger"/>
	      </rng:attribute>
	    </rng:interleave>
	  </content>
	</macroSpec>
	<macroSpec ident="att1offset" mode="add">
	  <content>
	    <rng:interleave>
	      <rng:attribute name="attribute.1">
		<rng:value type="token">byte_offset</rng:value>
	      </rng:attribute>
	      <rng:attribute name="attribute.2">
		<rng:data type="nonNegativeInteger"/>
	      </rng:attribute>
	      <rng:attribute name="attribute.3">
		<rng:data type="nonNegativeInteger"/>
	      </rng:attribute>
	    </rng:interleave>
	  </content>
	</macroSpec>
	<macroSpec ident="att1seconds" mode="add">
	  <content>
	    <rng:interleave>
	      <rng:attribute name="attribute.1">
		<rng:value type="token">time_in_sec</rng:value>
	      </rng:attribute>
	      <rng:attribute name="attribute.2">
		<rng:data type="integer"/>
	      </rng:attribute>
	      <rng:attribute name="attribute.3">
		<rng:data type="integer"/>
	      </rng:attribute>
	    </rng:interleave>
	  </content>
	</macroSpec>
	<elementSpec ident="hi" mode="change">
	  <altIdent>methodOne</altIdent>
	  <content>
	    <alternate minOccurs="1" maxOccurs="1">
	      <macroRef key="att1ptr"/>
	      <macroRef key="att1char"/>
	      <macroRef key="att1offset"/>
	      <macroRef key="att1seconds"/>
	    </alternate>
	    <macroRef key="macro.paraContent"/>
	  </content>
	  <remarks>
	    <p>The attributes on this element ...</p>
	  </remarks>
	</elementSpec>
	<!-- above is ODD hack; below is PureODD with Schematron -->
	<classSpec ident="att.aPureODDmethod" type="atts" mode="add">
	  <constraintSpec scheme="schematron" ident="two-and-three-match-one">
	    <constraint>
	      <sch:rule context="*[@anotherAtt.1 eq 'pointer']">
		<sch:assert test="@anotherAtt.2 castable as xsd:anyURI">when @anotherAtt.1 is 'poitner' @anotherAtt.2 must be a URI</sch:assert>
		<sch:assert test="@anotherAtt.3 castable as xsd:anyURI">when @anotherAtt.1 is 'poitner' @anotherAtt.3 must be a URI</sch:assert>
	      </sch:rule>
	      <sch:rule context="*[@anotherAtt.1 = ('character','byte_offset')]">
		<sch:assert test="@anotherAtt.2 castable as xsd:nonNegativeInteger">when @anotherAtt.1 is '<sch:value-of select="@anotherAtt.1"/>' @anotherAtt.2 must be a counting number</sch:assert>
		<sch:assert test="@anotherAtt.3 castable as xsd:nonNegativeInteger">when @anotherAtt.1 is '<sch:value-of select="@anotherAtt.1"/>' @anotherAtt.3 must be a counting number</sch:assert>
	      </sch:rule>
	      <sch:rule context="*[@anotherAtt.1 eq 'time_in_sec']">
		<sch:assert test="@anotherAtt.2 castable as xsd:integer">when @anotherAtt.1 is 'time_in_sec' @anotherAtt.2 must be an integer</sch:assert>
		<sch:assert test="@anotherAtt.3 castable as xsd:integer">when @anotherAtt.1 is 'time_in_sec' @anotherAtt.3 must be an integer</sch:assert>
	      </sch:rule>
	    </constraint>
	  </constraintSpec>
	  <attList>
	    <attDef ident="anotherAtt.1">
	      <datatype minOccurs="1" maxOccurs="1">
		<dataRef key="att1types"/>
	      </datatype>
	    </attDef>
	    <attDef ident="anotherAtt.2">
	      <datatype minOccurs="1" maxOccurs="1">
		<dataRef key="att23types"/>
	      </datatype>
	    </attDef>
	    <attDef ident="anotherAtt.3">
	      <datatype minOccurs="1" maxOccurs="1">
		<dataRef key="att23types"/>
	      </datatype>
	    </attDef>
	  </attList>
	</classSpec>
	<dataSpec ident="att1types">
	  <content>
	    <valList type="closed">
	      <valItem ident="pointer"/>
	      <valItem ident="character"/>
	      <valItem ident="byte_offset"/>
	      <valItem ident="time_in_sec"/>
	    </valList>
	  </content>
	</dataSpec>
	<dataSpec ident="att23types">
	  <content>
	    <alternate>
	      <dataRef key="teidata.pointer"/>
	      <dataRef key="teidata.count"/>
	      <dataRef name="integer"/>
	    </alternate>
	  </content>
	</dataSpec>
	<elementSpec ident="q" mode="change">
	  <altIdent>methodTwo</altIdent>
	  <classes mode="change">
	    <memberOf key="att.aPureODDmethod"/>
	  </classes>
	</elementSpec>
      </schemaSpec>
    </body>
  </text>
</TEI>