Merge pull request #3183 from akhtars/master

Add support for Schematron XML validation files (.sch)
This commit is contained in:
Arfon Smith
2016-09-06 14:22:49 -07:00
committed by GitHub
4 changed files with 443 additions and 0 deletions

207
samples/XML/HITSP_C32.sch Normal file
View File

@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
Retrieved on 2016-08-30 from http://cda-validation.nist.gov/cda-validation/downloads.html.
Disclaimer from the National Institute of Standards and Technology website:
"Unless otherwise noted, this software was developed at the National Institute of Standards and Technology by employees of the Federal Government in the course of their official duties. Pursuant to title 17 Section 105 of the United States Code this software is not subject to copyright protection and is in the public domain. The CDA Guideline Validator is an experimental system. NIST assumes no responsibility whatsoever for its use by other parties, and makes no guarantees, expressed or implied, about its quality, reliability, or any other characteristic. We would appreciate acknowledgement if the software is used."
-->
<!DOCTYPE schema [
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.1 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.1.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.2 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.2.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.3 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.3.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.4 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.4.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.5 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.5.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.6 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.6.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.7 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.7.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.8 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.8.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.9 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.9.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.10 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.10.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.11 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.11.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.12 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.12.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.13 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.13.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.14 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.14.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.15 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.15.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.16 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.16.ent'>
<!ENTITY ent-2.16.840.1.113883.3.88.11.32.17 SYSTEM 'templates/2.16.840.1.113883.3.88.11.32.17.ent'>
]>
<schema xmlns="http://www.ascc.net/xml/schematron" xmlns:cda="urn:hl7-org:v3">
<!--
To use iso schematron instead of schematron 1.5,
change the xmlns attribute from
"http://www.ascc.net/xml/schematron"
to
"http://purl.oclc.org/dsdl/schematron"
-->
<title>HITSP_C32</title>
<ns prefix="cda" uri="urn:hl7-org:v3"/>
<ns prefix="sdtc" uri="urn:hl7-org:sdtc"/>
<ns prefix="xsi" uri="http://www.w3.org/2001/XMLSchema-instance"/>
<phase id='errors'>
<active pattern='p-2.16.840.1.113883.3.88.11.32.1-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.2-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.3-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.4-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.5-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.6-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.7-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.8-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.9-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.10-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.11-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.12-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.13-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.14-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.15-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.16-errors'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.17-errors'/>
</phase>
<phase id='warning'>
<active pattern='p-2.16.840.1.113883.3.88.11.32.1-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.2-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.3-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.4-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.5-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.6-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.7-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.8-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.9-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.10-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.11-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.12-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.13-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.14-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.15-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.16-warning'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.17-warning'/>
</phase>
<phase id='note'>
<active pattern='p-2.16.840.1.113883.3.88.11.32.1-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.2-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.3-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.4-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.5-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.6-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.7-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.8-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.9-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.10-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.11-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.12-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.13-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.14-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.15-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.16-note'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.17-note'/>
</phase>
<phase id='violation'>
<active pattern='p-2.16.840.1.113883.3.88.11.32.4-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.5-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.6-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.7-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.8-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.9-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.10-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.11-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.12-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.13-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.14-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.15-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.16-violation'/>
<active pattern='p-2.16.840.1.113883.3.88.11.32.17-violation'/>
</phase>
<!-- Template_2.16.840.1.113883.3.88.11.32.1 -->
<!-- HITSP/C32 Registration and Medication History -->
&ent-2.16.840.1.113883.3.88.11.32.1;
<!-- Template_2.16.840.1.113883.3.88.11.32.2 -->
<!-- HITSP/C32 Language Spoken Module -->
&ent-2.16.840.1.113883.3.88.11.32.2;
<!-- Template_2.16.840.1.113883.3.88.11.32.3 -->
<!-- HITSP/C32 Support Module -->
&ent-2.16.840.1.113883.3.88.11.32.3;
<!-- Template_2.16.840.1.113883.3.88.11.32.4 -->
<!-- HITSP/C32 Healthcare Provider Module -->
&ent-2.16.840.1.113883.3.88.11.32.4;
<!-- Template_2.16.840.1.113883.3.88.11.32.5 -->
<!-- HITSP/C32 Insurance Provider Module -->
&ent-2.16.840.1.113883.3.88.11.32.5;
<!-- Template_2.16.840.1.113883.3.88.11.32.6 -->
<!-- HITSP/C32 Allergies and Drug Sensitivities Module -->
&ent-2.16.840.1.113883.3.88.11.32.6;
<!-- Template_2.16.840.1.113883.3.88.11.32.7 -->
<!-- HITSP/C32 Conditions Module -->
&ent-2.16.840.1.113883.3.88.11.32.7;
<!-- Template_2.16.840.1.113883.3.88.11.32.8 -->
<!-- HITSP/C32 Medications - Administration Information Module -->
&ent-2.16.840.1.113883.3.88.11.32.8;
<!-- Template_2.16.840.1.113883.3.88.11.32.9 -->
<!-- HITSP/C32 Medications Module, Medication Information data element -->
&ent-2.16.840.1.113883.3.88.11.32.9;
<!-- Template_2.16.840.1.113883.3.88.11.32.10 -->
<!-- HITSP/C32 Medications Module, Medication Information, Type of Medication data element -->
&ent-2.16.840.1.113883.3.88.11.32.10;
<!-- Template_2.16.840.1.113883.3.88.11.32.11 -->
<!-- HITSP/C32 Order Information data element -->
&ent-2.16.840.1.113883.3.88.11.32.11;
<!-- Template_2.16.840.1.113883.3.88.11.32.12 -->
<!-- HITSP/C32 Comments Module -->
&ent-2.16.840.1.113883.3.88.11.32.12;
<!-- Template_2.16.840.1.113883.3.88.11.32.13 -->
<!-- HITSP/C32 Advance Directives Module -->
&ent-2.16.840.1.113883.3.88.11.32.13;
<!-- Template_2.16.840.1.113883.3.88.11.32.14 -->
<!-- HITSP/C32 Immunizations Module -->
&ent-2.16.840.1.113883.3.88.11.32.14;
<!-- Template_2.16.840.1.113883.3.88.11.32.15 -->
<!-- HITSP/C32 Vital Signs Module -->
&ent-2.16.840.1.113883.3.88.11.32.15;
<!-- Template_2.16.840.1.113883.3.88.11.32.16 -->
<!-- HITSP/C32 Results Module -->
&ent-2.16.840.1.113883.3.88.11.32.16;
<!-- Template_2.16.840.1.113883.3.88.11.32.17 -->
<!-- HITSP/C32 Encounters Module -->
&ent-2.16.840.1.113883.3.88.11.32.17;
</schema>

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://purl.oclc.org/dsdl/schematron"
queryBinding="xslt2"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- XSLT 2.0 Schematron by Wendell Piez (Mulberry Technologies, Inc.),
August 2011 -->
<!-- This Schematron is released into the public domain.
Please credit your sources. -->
<!-- Requires Schematron that allows embedded XSLT 2.0 with support for
the namespace:: axis. Tested with Saxon 9.3.0.5. -->
<ns prefix="m" uri="http://www.mulberrytech.com/xslt/util"/>
<xsl:variable name="ns-set" xmlns:m="http://www.mulberrytech.com/xslt/util">
<!-- include elements to declare expected namespace prefixes
with their bindings, like so:
(prefix 'mml')
<m:ns prefix="mml" uri="http://www.w3.org/1998/Math/MathML"/>
(default namespace, no prefix)
<m:ns prefix="" uri="http://www.w3.org/1998/Math/MathML"/>
-->
<m:ns prefix="mml" uri="http://www.w3.org/1998/Math/MathML"/>
<m:ns prefix="xlink" uri="http://www.w3.org/1999/xlink"/>>
<m:ns prefix="oasis" uri="http://docs.oasis-open.org/ns/oasis-exchange/table"/>
<!-- 'xml' prefix is always in scope with this binding -->
<m:ns prefix="xml" uri="http://www.w3.org/XML/1998/namespace"/>
</xsl:variable>
<pattern>
<!-- checking the document element against the spec given in ns-set -->
<rule context="/*">
<let name="new-prefixes" value="in-scope-prefixes(.)[not(. = $ns-set/m:ns/@prefix)]"/>
<let name="new-namespaces" value="(for $p in (in-scope-prefixes(.)) return namespace-uri-for-prefix($p,.))
[not(. = $ns-set/m:ns/@uri)]"/>
<report test="exists($new-prefixes)">
Unrecognized namespace prefix<xsl:value-of select="('es')[count($new-prefixes) gt 1]"/>:
<value-of select="string-join(
(for $p in $new-prefixes return m:label($p)),
', ')"/>
</report>
<report test="exists($new-namespaces)">
Unrecognized namespace URI<xsl:value-of select="('s')[count($new-namespaces) gt 1]"/>:
<value-of select="string-join(($new-namespaces),', ')"/>
</report>
<let name="misassigned-prefixes" value="in-scope-prefixes(.)[not(.=$new-prefixes)]
[for $p in (.) return
namespace-uri-for-prefix($p,current()) ne $ns-set/m:ns[@prefix=$p]/@uri]"/>
<report test="exists($misassigned-prefixes)">
Prefix<xsl:value-of select="('es')[count($misassigned-prefixes) gt 1]"/>
assigned incorrectly: <value-of select="string-join(
(for $p in $misassigned-prefixes return
concat(m:label($p), ' (should be ''',$ns-set/m:ns[@prefix=$p]/@uri,''')')),
'; ')"/>
</report>
</rule>
<!-- Elsewhere, all namespaces given must correspond with those
on the parent -->
<rule context="*">
<assert test="every $n in (namespace::*) satisfies
exists(../namespace::*[deep-equal(.,$n)])">
Namespace may not be declared here.
</assert>
<assert test="every $n in (../namespace::*) satisfies
exists(namespace::*[deep-equal(.,$n)])">
Namespace may not be undeclared here.
</assert>
</rule>
</pattern>
<xsl:function name="m:label" as="xs:string">
<xsl:param name="label" as="xs:string"/>
<xsl:sequence select="if (string($label)) then concat('''',$label,'''') else '[unprefixed]'"/>
</xsl:function>
</schema>

151
samples/XML/oasis-table.sch Normal file
View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- ============================================================= -->
<!--
This work is in the public domain and may be reproduced, published or
otherwise used without the permission of the National Library of Medicine (NLM).
We request only that the NLM is cited as the source of the work.
Although all reasonable efforts have been taken to ensure the accuracy and
reliability of the software and data, the NLM and the U.S. Government do
not and cannot warrant the performance or results that may be obtained by
using this software or data. The NLM and the U.S. Government disclaim all
warranties, express or implied, including warranties of performance,
merchantability or fitness for any particular purpose.
-->
<!-- ============================================================= -->
<schema xmlns="http://purl.oclc.org/dsdl/schematron"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://mulberrytech.com/xslt/oasis-html/util"
queryBinding="xslt2">
<title>OASIS/CALS table validation</title>
<!-- Mulberry Technologies (wap)
Designed for JATS, but probably also useful in other
systems using OASIS/CALS/Docbook tables
Assumes a table DTD (or schema) valid to the OASIS/CALS/Docbook model
-->
<ns prefix="o" uri="http://docs.oasis-open.org/ns/oasis-exchange/table"/>
<ns prefix="m" uri="http://mulberrytech.com/xslt/oasis-html/util"/>
<!--<ns prefix="xsl" uri="http://www.w3.org/1999/XSL/Transform"/>-->
<!-- the included stylesheet includes key and function declarations required -->
<xsl:include href="oasis-exchange-support.xsl"/>
<let name="default-border-style" value="solid"/>
<pattern>
<rule context="o:tgroup">
<let name="okay-cols" value="@cols[. castable as xs:integer][. > 0]"/>
<assert test="exists(@cols)">tgroup/@cols is not given</assert>
<assert test="empty(@cols) or exists($okay-cols)">@cols should be a natural number
(integer greater than zero).</assert>
<assert test="empty($okay-cols) or empty(o:colspec) or (count(o:colspec) = @cols)">The tgroup has
<value-of select="count(o:colspec)"/> colspec<value-of select="if (count(o:colspec) = 1) then '' else 's'"/>,
but its @cols is given as '<value-of select="@cols"/>'.</assert>
<report test="not(*/o:row/m:actual-cols(.) != */o:row/m:actual-cols(.))
and ($okay-cols != m:actual-cols((*/o:row)[1]))">tgroup/@cols is given as
<value-of select="$okay-cols"/>, but all rows have <value-of
select="m:actual-cols((*/o:row)[1])"/> entr<value-of
select="if (m:actual-cols((*/o:row)[1]) eq 1) then 'y' else 'ies'"/>.
</report>
<report test="@align='char'" role="warning">Without assigning @char or @charoff to everything,
assigning @align='char' to tgroup only aligns contents to right of center.</report>
</rule>
<rule context="o:colspec">
<let name="okay-colwidth"
value="@colwidth[exists(m:colwidth-unit(current()))]"/>
<assert test="empty(@colwidth) or exists($okay-colwidth)">Malformed @colwidth.</assert>
<assert test="empty($okay-colwidth) or
(count(../o:colspec[m:colwidth-unit(.)=m:colwidth-unit(current())]) &gt;
count(../o:colspec[not(m:colwidth-unit(.)=m:colwidth-unit(current()))]))">@colwidth unit
(<value-of select="m:colwidth-unit(.)"/>) is not consistent with the
units on other colspecs.</assert>
<assert test="empty($okay-colwidth) or matches($okay-colwidth,'^\s*\*\s*$')
or (xs:double(replace($okay-colwidth,'[\s\p{L}\*]','')[. castable as xs:double]) &gt; 0)">@colwidth must be positive</assert>
<report test="empty(m:colwidth-unit(.))
and exists(../o:colspec/m:colwidth-unit(.))">The same unit of measure should be used on every
colspec/@colwidth.</report>
<assert test="empty(@colnum) or (@colnum = count(.|preceding-sibling::o:colspec))">@colnum
'<value-of select="@colnum"/>' does not correspond to
the column's actual number (<value-of select="count(.|preceding-sibling::o:colspec)"/>)</assert>
<report test="@colname = (../o:colspec except .)/@colname">The same @colname is assigned to more than
one colspec.</report>
<assert test="not(@align='char') or exists(@char)" role="warning">@align='char', but no @char is given.</assert>
<report test="normalize-space(@char) and not((@align,../@align)[1]='char')">@char is given, but alignment is not 'char'.</report>
<assert test="empty(@charoff) or ((@align,../@align)[1]='char')" role="warning">@charoff is given, but alignment is not 'char'.</assert>
</rule>
<rule context="o:row">
<let name="tgroup" value="ancestor::o:tgroup[1]"/>
<let name="rowno" value="m:rowno(.)"/>
<let name="given-entries" value="count(distinct-values(key('entry-by-row',$rowno,$tgroup)/m:across(.)))"/>
<report test="$given-entries &lt; $tgroup/@cols
and exists($tgroup/@cols[. castable as xs:integer])" role="warning">
The row doesn't have enough entries (<value-of select="$tgroup/@cols"/>
<value-of select="if ($tgroup/@cols=1) then ' is' else ' are'"/> expected;
<value-of select="$given-entries"/> <value-of select="if ($given-entries=1) then ' is' else ' are'"/> given).
</report>
</rule>
<rule context="o:entry">
<let name="tgroup" value="ancestor::o:tgroup[1]"/>
<assert test="empty(@nameend) or exists(key('colspec-by-name',@nameend,$tgroup))">No colspec is
named <value-of select="@nameend"/>.</assert>
<assert test="empty(@nameend|@namest) or
(key('colspec-by-name',@nameend,$tgroup) >> key('colspec-by-name',@namest,$tgroup))">Entry's end
column (<value-of select="@nameend"/>) must follow its start column
(<value-of select="@namest"/>).</assert>
<assert test="empty(@namest) or exists(key('colspec-by-name',@namest,$tgroup))">No colspec is
named <value-of select="@namest"/>.</assert>
<assert test="empty(@colname) or exists(key('colspec-by-name',@colname,$tgroup))">No colspec is
named <value-of select="@colname"/>.</assert>
<assert test="empty(@nameend) or exists(@colname|@namest)">Entry is assigned an end
column (<value-of select="@nameend"/>) but not a start column.</assert>
<assert test="not(@colname != @namest)">Entry is assigned to column <value-of select="@colname"/>,
so it can't start at column <value-of select="@namest"/>.
</assert>
<assert test="m:across(.)[1] &gt; (preceding-sibling::o:entry[1]/m:across(.)[last()],0)[1]">
Entry must be assigned to a free column (after its preceding entries).
</assert>
<report test="m:down(.) &gt; m:rowno(../../o:row[last()])">This entry doesn't fit into
its <value-of select="local-name(../..)"/>.</report>
<report test="(exists(@morerows) and
(key('entry-by-row',m:down(.),$tgroup)/m:across(.)[last()] &gt; $tgroup/@cols))
or empty($tgroup/@cols)" role="warning">
A row in which this entry appears has too many entries.
</report>
<!-- the next rule will never fire for entries spanning columns: they always
fit by virtue of being assigned a @nameend -->
<report test="(m:across(.)[last()] &gt; $tgroup/@cols) or empty($tgroup/@cols)">
Entry does not fit in row. (<value-of select="$tgroup/@cols"/> are allowed; entry
is in column <value-of select="m:across(.)[last()]"/>.)
<!-- Entry does not fit in row. (# columns are allowed; row ends in column #.) -->
</report>
<assert test="empty(@char) or m:align(.)='char'" role="warning">@char is given, but alignment is not 'char'.</assert>
<assert test="empty(@charoff) or m:align(.)='char'" role="warning">@charoff is given, but alignment is not 'char'.</assert>
<assert test="empty(@charoff) or ((@charoff castable as xs:integer) and
(@charoff &gt;= 0) and (@charoff &lt;= 100))">@charoff must be a whole number between 0 and 100.</assert>
<assert test="not(m:align(.)='char') or exists(@char|m:colspec-for-entry(.)/@char)" role="warning">
Entry is designated for character alignment, but no character (@char) is given on it or its colspec.
</assert>
<assert test="empty(@char) or not(@char != m:colspec-for-entry(.)/@char)">
Entry is assigned an alignment character (<value-of select="@char"/>)
different from its column's (<value-of select="m:colspec-for-entry(.)/@char"/>).</assert>
<report test="exists(*) and (m:align(.)='char')" role="warning">With @align='char', markup of
entry contents (<value-of select="string-join(distinct-values(*/name()),', ')"/>) will be ignored.</report>
</rule>
</pattern>
</schema>