Deep inclusions

From W3C Wiki
Jump to: navigation, search

Deep inclusions (a co-constraint use case)

Allow an element (e.g. noteref) to appear in certain locations (e.g. within paragraphs) and not in others (e.g. within headings), but only as a descendant of a particular enabling ancestor (e.g. body).

source: Fabio Vitali


Other use cases: Co-constraint Use Cases

Description

   Within paragraphs in the main text, at any depth, BUT NOT within headings,
   footnotes and bibliography, element <noteref> may appear. Note that other
   inline elements such as <bold> and <italic> may appear in all situations, and
   <noteref> may appear within <bold> or <italic> elements.

Analysis

(Add your analysis here; see your name in pixels!)

MSM

Another real-world example of this use case: the HTML input element, which is allowed within div and p and li and so on (pretty much: wherever `#PCDATA` is allowed), inside a form element. If the div or p or li is not inside a form, then input is not allowed.

Different definitions of HTML have handled this differently. In one early DTD (2.0, perhaps), input was not included in the content model of p etc., but was an inclusion on the form element. In accordance with the ISO 8879 rules for inclusions, this meant that input was legal in all the places described above -- but it was also legal anywhere else inside a form, even in places where `#PCDATA` was not allowed (e.g. between list items). A prose constraint was needed to say input was not really legal everywhere the DTD said it was legal.

A later DTD (3.2?) did it the other way round: input appeared in the content models of div, p, etc., and a prose constraint was added to say that really it was legal in those places only if a form element was open.

Since 'only if a form element is open' is simpler to understand than 'only where #PCDATA can legally occur', the second formulation is perhaps better than the first. But neither formulation actually captures the rule of the language: both require additional prose constraints.

The existence of local element/type bindings in XML Schema (and local gi/nonterminal bindings in Relax NG) means that this problem can be solved in XML Schema 1.0 and in Relax NG. An XSD solution is sketched in my paper Context-sensitive rules in XML Schema. Murata Makoto produced a paper showing a Relax NG solution to the problem; I believe the solution was structurally similar (using nonterminals roughly as the XSD solution uses types), but I cannot now locate the paper; it may be the paper at [1].

Possible solutions

Relax NG

Schematron

Perhaps the simplest Schematron solution for this problem is to place an assertion like the following on the declaration for the input element:


  <report test="not(ancestor::html:form)">The 'input' 
    element is only legal inside a 'form' element.</report>


If we wish to prohibit reference to ancestors and siblings (and more generally to anything outside the tree rooted in the element being tested), the XPath expression can be reformulated and placed on the HTML body or html element:


  <report test="not(.//html:input[not(./ancestor::html:form])">
   This element contains 'input' elements which are not nested
   inside a 'form' element.</report>

or

  <assert test="count(.//html:input[ancestor::html:form])
                = count(.//html:input)">
   Every HTML 'input' element inside this element must also
   be inside an HTML 'form' element.
  </assert>


Some observers believe that the latter formulations, placed on the body or html element, are clearer than the initial formulation intended to be placed on the input element. Others believe the opposite and suggest that the user will be helped better by an error message on the misplaced input element than by an error on the enclosing html or body element.

(Solution proposed by MSM; not checked.)

Check clause

To support this use case using the check-clause proposal in the Working Draft of 31 August 2006, the constraint must be rewritten to point only downward:

  <assert test="count(.//html:form//html:input)
                = count(.//html:input)"/>


This may or may not be regarded as an easy rewrite, but it took the author of this discussion a few hours before it became clear.

(Solution proposed by MSM; not checked.)

SchemaPath

Conditional Type