Exclude anywhere

From W3C Wiki

Exclude anywhere (a co-constraint use case)

Make the legality of certain elements depend not only upon the content model of their parent but upon the presence or absence of elements or values elsewhere in the document.

Cf. Deep inclusions and the HTML input element (described there).

source: Fabio Vitali


Other use cases: Co-constraint Use Cases

Description

   Within a contract, which is a plain text document with some hierarchy and
   typographical styles, some elements may not appear depending on the context.
   For instance, individual parties in a contract must be identified by their
   SSN, while private enterprises must be identified by their VAT number.
   Individuals have no VAT number, enterprises have no SSN. These values can
   appear next to the name and address of the parties, or quite far away,
   depending on the style and whim of the lawyer writing the contract.

(Elaboration by MSM & FV) For example, given a section type which allows paragraphs and nested sections, and a paragraph type which allows SSN, VATnum, italic, and bold elements (presumably among other phrase-level elements) and a party element in which the parties to the contract are structurally identified as either individuals or corporations (as in `<party type="individual">John Smith</party>` or `<party type="corporation">Acme, inc.</party>`), the constraint is:

  • SSN is legal in paragraphs or phrase-level elements only if at least one of the parties to the contract is an individual (so if `count(//party[@type='individual']) > 0`).
  • VATnum is legal in paragraphs or phrase-level elements only if at least one of the parties to the contract is an enterprise (so if `count(//party[@type='corporation']) > 0`).

Analysis

MSM

If alternatively we imagine distinct contract types with names like 'inter-corporate-contract' and 'inter-personal-contract', then we can achieve the constraint using local elements and a duplication of the relevant part of the type hierarchy, as outlined in [1]. The solution described there becomes rather clumsy, however, as soon as more than one or two such co-occurrence constraints are to be enforced.

FV

This is a real case being implemented now in a number of projects relating to legal documents. The point is that there cannot be any constraint as to where semantic elements (e.g., party, address, SSN, VATnum, etc.) appear in the document, anywhere in a number of different paeragraphs in different sub-sub-sections.

The people that created the schemas have come to a rather ingenuous solution, if incredibly tortuous and computationally intensive, to the problem of checking constraints on semantic elements appearing anywhere in the document. There exist two schemas, one including all and only presentational elements (i.e., section, para, bold, italic, etc., without party, address, SSN, VATnum, etc.), and one including the document element and all and only semantic elements (i.e., party, address, SSN, VATnum, etc.). Two XSLT stylesheets are used to remove semantic elements, respectively presentational elements. In order to validate the dcument structurally, all semantical elements are removed using XSLT #2, and the resulting document is validated against Schema #1. Conversely, using XSLT #1 you obtain a document without presentational elements that can be validated against Schema #2. A document is valid if and only if it passes both subvalidations.

Please note that this means that NO global schema for the document exists.

Possible solutions

Relax NG

Schematron

Check clause

SchemaPath

Three possible approach this time:


<xs:element name="root" type="tSection"/>

<xs:complexType name="tSection">
  <xs:choice maxOccurs="unbounded">
    <xs:element name="section" type="tSection"/>
    <xs:element name="p">
      <xs:alt cond="not(//party[@type='individual'])"  type="tInlineOnlyCorporations"/>
      <xs:alt cond="not(//party[@type='corporation'])" type="tInlineOnlyIndividuals"/>
      <xs:alt                                          type="tInlineAll"/>
  </xs:choice>
</xs:complexType>

<xs:complexType name="tInlineAll" mixed="true"/>
  <xs:choice maxOccurs="unbounded">
    <xs:group ref="inlinePresentation"/>
    <xs:element name="party" type="tInlineWithAttrs"/>
    <xs:element name="address" type="tInline"/>
    <xs:element name="SSN" type="tInline"/>
    <xs:element name="VATnum" type="tInline"/>
    ...
  </xs:choice>
</xs:complexType>

<xs:complexType name="tInlineOnlyCorporations" mixed="true"/>
  <xs:choice maxOccurs="unbounded">
    <xs:group ref="inlinePresentation"/>
    <xs:element name="party" type="tInlineWithAttrs"/>
    <xs:element name="address" type="tInline"/>
    <xs:element name="VATnum" type="tInline"/>
    ...
  </xs:choice>
</xs:complexType>

<xs:complexType name="tInlineOnlyIndividuals" mixed="true"/>
  <xs:choice maxOccurs="unbounded">
    <xs:group ref="inlinePresentation"/>
    <xs:element name="party" type="tInlineWithAttrs"/>
    <xs:element name="address" type="tInline"/>
    <xs:element name="SSN" type="tInline"/>
    ...
  </xs:choice>
</xs:complexType>

<xs:group name="inlinePresentation">
  <xs:choice maxOccurs="unbounded">
    <xs:element name="b" type="tInline"/>
    <xs:element name="i" type="tInline"/>
    ...
  <xs:choice>
</xs:group>


<xs:element name="root" type="tSection"/>

<xs:complexType name="tSection">
  <xs:choice maxOccurs="unbounded">
    <xs:element name="section" type="tSection"/>
    <xs:element name="p" type="tInline"/>
  </xs:choice>
</xs:complexType>

<xs:complexType name="tInline" mixed="true"/>
  <xs:choice maxOccurs="unbounded">
    <xs:group ref="inlinePresentation"/>
    <xs:group ref="inlineSemantic"/>
  </xs:choice>
</xs:complexType>

<xs:group name="inlinePresentation">
  <xs:choice maxOccurs="unbounded">
    <xs:element name="b" type="tInline"/>
    <xs:element name="i" type="tInline"/>
    ...
  <xs:choice>
</xs:group>

<xs:group name="inlineSemantic">
  <xs:choice maxOccurs="unbounded">
    <xs:element name="party" type="tInlineWithAttrs"/>
    <xs:element name="address" type="tInline"/>
    <xs:element name="SSN">
      <xs:alt cond="//party[@type='corporation']" type="xsd:error"/>
      <xs:alt                                     type="tInline"/>
    </xs:element>
    <xs:element name="VATnum">
      <xs:alt cond="//party[@type='individual']" type="xsd:error"/>
      <xs:alt                                    type="tInline"/>
    </xs:element>
    ...
  <xs:choice>
</xs:group>


<xs:element name="root">
  <xs:alt cond="//party[@type='individual']  and not(//SSN)"    type="xsd:error"/>
  <xs:alt cond="//party[@type='corporation'] and not(//VATnum)" type="xsd:error"/>
  <xs:alt                   type="tSection"/>

<xs:complexType name="tSection">
  <xs:choice maxOccurs="unbounded">
    <xs:element name="section" type="tSection"/>
    <xs:element name="p" type="tInline"/>
  </xs:choice>
</xs:complexType>

<xs:complexType name="tInline" mixed="true"/>
  <xs:choice maxOccurs="unbounded">
    <xs:group ref="inlinePresentation"/>
    <xs:group ref="inlineSemantic"/>
  </xs:choice>
</xs:complexType>

<xs:group name="inlinePresentation">
  <xs:choice maxOccurs="unbounded">
    <xs:element name="b" type="tInline"/>
    <xs:element name="i" type="tInline"/>
    ...
  <xs:choice>
</xs:group>

<xs:group name="inlineSemantic">
  <xs:choice maxOccurs="unbounded">
    <xs:element name="party" type="tInlineWithAttrs"/>
    <xs:element name="address" type="tInline"/>
    <xs:element name="SSN" type="tInline"/>
    <xs:element name="VATnum" type="tInline"/>
    ...
  <xs:choice>
</xs:group>


Conditional Type