Alternative enumerations

From W3C Wiki

Alternative enumerations (a co-constraint use case)

Make the set of enumerated values legal for one attribute depend on the value of a second attribute.

Cf. Enumeration or any string.

source: Fabio Vitali


Other use cases: Co-constraint Use Cases

Description

   Multiple keywords can take terms from different dictionaries. Dictionaries are
   partially overlapping, so some but not all terms are present in more than one
   dictionary. It is an error to indicate a term that does not belong to the
   specified dictionary.


    <keywords>
        <keyword dict="dict1" term="term1"/>
        <keyword dict="dict2" term="term1"/>
        ...
    </keywords>


(Elaboration by MSM:) To make a more concrete task for the sample solutions, let us imagine that we have three dictionaries for color names, each with an enumerated list of legal values. The dictionaries and their enumerations are:

  • html4.0: {aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white, yellow}.g
  • browsers: {AliceBlue, AntiqueWhite, Aqua, Aquamarine, Azure, Beige, Bisque, Black, BlanchedAlmond, Blue, BlueViolet, Brown, BurlyWood, CadetBlue, Chartreuse, Chocolate, Coral, CornflowerBlue, Cornsilk, Crimson, Cyan, DarkBlue, DarkCyan, DarkGoldenRod, DarkGray, DarkGreen, DarkKhaki, DarkMagenta, DarkOliveGreen, Darkorange, DarkOrchid, DarkRed, DarkSalmon, DarkSeaGreen, DarkSlateBlue, DarkSlateGray, DarkTurquoise, DarkViolet, DeepPink, DeepSkyBlue, DimGray, DodgerBlue, Feldspar, FireBrick, FloralWhite, ForestGreen, Fuchsia, Gainsboro, GhostWhite, Gold, GoldenRod, Gray, Green, GreenYellow, HoneyDew, HotPink, IndianRed, Indigo, Ivory, Khaki, Lavender, LavenderBlush, LawnGreen, LemonChiffon, LightBlue, LightCoral, LightCyan, LightGoldenRodYellow, LightGrey, LightGreen, LightPink, LightSalmon, LightSeaGreen, LightSkyBlue, LightSlateBlue, LightSlateGray, LightSteelBlue, LightYellow, Lime, LimeGreen, Linen, Magenta, Maroon, MediumAquaMarine, MediumBlue, MediumOrchid, MediumPurple, MediumSeaGreen, MediumSlateBlue, MediumSpringGreen, MediumTurquoise, MediumVioletRed, MidnightBlue, MintCream, MistyRose, Moccasin, NavajoWhite, Navy, OldLace, Olive, OliveDrab, Orange, OrangeRed, Orchid, PaleGoldenRod, PaleGreen, PaleTurquoise, PaleVioletRed, PapayaWhip, PeachPuff, Peru, Pink, Plum, PowderBlue, Purple, Red, RosyBrown, RoyalBlue, SaddleBrown, Salmon, SandyBrown, SeaGreen, SeaShell, Sienna, Silver, SkyBlue, SlateBlue, SlateGray, Snow, SpringGreen, SteelBlue, Tan, Teal, Thistle, Tomato, Turquoise, Violet, VioletRed, Wheat, White, WhiteSmoke, Yellow, YellowGreen}
  • x11: {AliceBlue, AntiqueWhite, Aqua, Aquamarine, Azure, Beige, Bisque, Black, BlanchedAlmond, Blue, BlueViolet, Brown, BurlyWood, CadetBlue, Chartreuse, Chocolate, Coral, CornflowerBlue, Cornsilk, Crimson, Cyan, DarkBlue, DarkCyan, DarkGoldenrod, DarkGray, DarkGreen, DarkKhaki, DarkMagenta, DarkOliveGreen, DarkOrange, DarkOrchid, DarkRed, DarkSalmon, DarkSeaGreen, DarkSlateBlue, DarkSlateGray, DarkTurquoise, DarkViolet, DeepPink, DeepSkyBlue, DimGray, DodgerBlue, FireBrick, FloralWhite, ForestGreen, Fuchsia, Gainsboro, GhostWhite, Gold, Goldenrod, Gray, Green, GreenYellow, Honeydew, HotPink, IndianRed, Indigo, Ivory, Khaki, Lavender, LavenderBlush, LawnGreen, LemonChiffon, LightBlue, LightCoral, LightCyan, LightGoldenrodYellow, LightGreen, LightGrey, LightPink, LightSalmon, LightSeaGreen, LightSkyBlue, LightSlateGray, LightSteelBlue, LightYellow, Lime, LimeGreen, Linen, Magenta, Maroon, MediumAquamarine, MediumBlue, MediumOrchid, MediumPurple, MediumSeaGreen, MediumSlateBlue, MediumSpringGreen, MediumTurquoise, MediumVioletRed, MidnightBlue, MintCream, MistyRose, Moccasin, NavajoWhite, Navy, OldLace, Olive, OliveDrab, Orange, OrangeRed, Orchid, PaleGoldenrod, PaleGreen, PaleTurquoise, PaleVioletRed, PapayaWhip, PeachPuff, Peru, Pink, Plum, PowderBlue, Purple, Red, RosyBrown, RoyalBlue, SaddleBrown, Salmon, SandyBrown, SeaGreen, Seashell, Sienna, Silver, SkyBlue, SlateBlue, SlateGray, Snow, SpringGreen, SteelBlue, Tan, Teal, Thistle, Tomato, Turquoise, Violet, Wheat, White, WhiteSmoke, Yellow, YellowGreen}

Note that the 'browser' list (taken from http://www.w3schools.com/html/html_colornames.asp) and the X11 list (from http://en.wikipedia.org/wiki/X11_Color_Names) are not completely identical: there are case differences in some tokens, and the browser list has three items which do not appear in the X11 list.

The requirement is that the value of the term attribute should come from the list associated with the dictionary named in the dict attribute.

Analysis

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

Paul Biron

This is soluble by current means.

Assume types my:html40_color, my:defacto_color, X11_color, defined as shown in the SchemaPath description below. Define a union of these types:


 <xsd:simpleType>
  <xsd:union memberTypes="my:html40_colors my:X11_colors my:defacto_colors"/>
 </xsd:simpleType>


and use xsi:type instead of dict.

Possible solutions

Relax NG

Grammaticalization

This can be handled by Relax-style grammaticalization, although some may find the formulation a little fiddly. To keep the main declarations short, we first define three different types for the three dictionaries; each type has a single token in its value space:


 <xsd:simpleType name="html40_dictionary">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="html40">
    <xsd:annotation>
     <xsd:documentation>Names defined in HTML 4.0</xsd:documentation>
    </xsd:annotation>
   </xsd:enumeration>
  </xsd:restriction>
 </xsd:simpleType>

 <xsd:simpleType name="X11_dictionary">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="x11">
    <xsd:annotation>
     <xsd:documentation>Names defined by X11</xsd:documentation>
    </xsd:annotation>
   </xsd:enumeration>
  </xsd:restriction>
 </xsd:simpleType>

 <xsd:simpleType name="defacto_dictionary">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="browsers">
    <xsd:annotation>
     <xsd:documentation>Names widely supported in browsers</xsd:documentation>
    </xsd:annotation>
   </xsd:enumeration>
  </xsd:restriction>
 </xsd:simpleType>

Now the declaration of the element uses a simple choice.

 <xsd:element name="keyword">
  <xsd:complexType>
   <xsd:choice>
    <xsd:sequence>
     <xsd:attribute name="dict" type="my:html40_dictionary"/>
     <xsd:attribute name="term" type="my:html40_color"/>
    </xsd:sequence>
    <xsd:sequence>
     <xsd:attribute name="dict" type="my:X11_dictionary"/>
     <xsd:attribute name="term" type="my:X11_color"/>
    </xsd:sequence>
    <xsd:sequence>
     <xsd:attribute name="dict" type="my:defacto_dictionary"/>
     <xsd:attribute name="term" type="my:defacto_color"/>
    </xsd:sequence>
   </xsd:choice>
  </xsd:complexType>
 </xsd:element>


Schematron

Check clause

One possible check-clause solution is the following. Note that the enumeration information has moved out of the type system and into the check clause.

For clarity, the middle of the two long lists has been elided.


 <xsd:complexType>
  <xsd:annotation><xsd:appinfo>
    <sch:pattern><sch:rule>
      <sch:report test="
       (@dict='html40' 
        and not(contains(' aqua black blue fuchsia gray green lime maroon -
                       navy olive purple red silver teal white yellow ',
                     concat(' ',@term,' '))))
       ">If @dict is 'html40', then @term must be one of aqua, black, blue, 
         fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver,
         teal, white, or yellow.</sch:report>
      <sch:report test="
       (@dict='browsers' 
        and not(contains(' AliceBlue AntiqueWhite Aqua Aquamarine Azure -
                         Beige Bisque Black BlanchedAlmond Blue BlueViolet -
                         ...
                         Snow SpringGreen SteelBlue Tan Teal Thistle Tomato Turquoise -
                         Violet VioletRed Wheat White WhiteSmoke Yellow YellowGreen ',
                     concat(' ',@term,' '))))
       ">If @dict is 'browsers', then @term must be one of the names defined
         by the 'browsers' list (see the documentation).</sch:report>
      <sch:report test="
       (@dict='x11' 
        and not(contains(' AliceBlue AntiqueWhite Aqua Aquamarine Azure -
                         Beige Bisque Black BlanchedAlmond Blue BlueViolet -
                         ...
                         SteelBlue Tan Teal Thistle Tomato Turquoise Violet -
                         Wheat White WhiteSmoke Yellow YellowGreen ',
                     concat(' ',@term,' '))))
       ">If @dict is 'x11, then @term must be one of the names defined
         by X11 (see the documentation for full list).</sch:report>
     </sch:rule></sch:pattern>
   </xsd:appinfo></xsd:annotation>

  <xsd:attribute name="term" type="xsd:NCName"/>
  <xsd:attribute name="dict">
   <xsd:simpleType>
    <xsd:restriction base="xsd:string">
     <xsd:enumeration value="html40">
      <xsd:annotation>
       <xsd:documentation>Names defined in HTML 4.0</xsd:documentation>
      </xsd:annotation>
     </xsd:enumeration>
     <xsd:enumeration value="browsers">
      <xsd:annotation>
       <xsd:documentation>Names widely supported in browsers</xsd:documentation>
      </xsd:annotation>
     </xsd:enumeration>
     <xsd:enumeration value="x11">
      <xsd:annotation>
       <xsd:documentation>Names defined by X11</xsd:documentation>
      </xsd:annotation>
     </xsd:enumeration>
    </xsd:restriction>
   </xsd:simpleType>
  </xsd:attribute>


(Solution offered by MSM)

SchemaPath

One possible SchemaPath solution to this is as follows. First, define an enumerated set of values for the dictionary names:

 <xsd:simpleType name="color_dictionaries">
  <xsd:restriction base="xsd:string">
   <xsd:enumeration value="html40">
    <xsd:annotation>
     <xsd:documentation>Names defined in HTML 4.0</xsd:documentation>
    </xsd:annotation>
   </xsd:enumeration>
   <xsd:enumeration value="browsers">
    <xsd:annotation>
     <xsd:documentation>Names widely supported in browsers</xsd:documentation>
    </xsd:annotation>
   </xsd:enumeration>
   <xsd:enumeration value="x11">
    <xsd:annotation>
     <xsd:documentation>Names defined by X11</xsd:documentation>
    </xsd:annotation>
   </xsd:enumeration>
  </xsd:restriction>
 </xsd:simpleType>


Then define three enumerations of the color terms available in each dictionary (the lists are abbreviated here for obvious reasons):

 <xsd:simpleType name="html40_color">
  <xsd:restriction base="xsd:NCName">
   <xsd:enumeration value="aqua"/>
   <xsd:enumeration value="black"/> 
   ...
   <xsd:enumeration value="white"/> 
   <xsd:enumeration value="yellow"/> 
  </xsd:restriction>
 </xsd:simpleType>
 <xsd:simpleType name="defacto_color">
  <xsd:restriction base="xsd:NCName"> 
   <xsd:enumeration value="AliceBlue"/> 
   <xsd:enumeration value="AntiqueWhite"/> 
   ...
   <xsd:enumeration value="DodgerBlue"/> 
   <xsd:enumeration value="Feldspar"/> 
   <xsd:enumeration value="FireBrick"/> 
   ...
   <xsd:enumeration value="White"/> 
   <xsd:enumeration value="WhiteSmoke"/> 
   <xsd:enumeration value="Yellow"/> 
   <xsd:enumeration value="YellowGreen"/> 
  </xsd:restriction>
 </xsd:simpleType>
 <xsd:simpleType name="X11_color">
  <xsd:restriction base="xsd:NCName"> 
   <xsd:enumeration value="AliceBlue"/> 
   <xsd:enumeration value="AntiqueWhite"/> 
   ...
   <xsd:enumeration value="DodgerBlue"/> 
   <xsd:enumeration value="FireBrick"/> 
   ...
   <xsd:enumeration value="White"/> 
   <xsd:enumeration value="WhiteSmoke"/> 
   <xsd:enumeration value="Yellow"/> 
   <xsd:enumeration value="YellowGreen"/> 
  </xsd:restriction>
 </xsd:simpleType>


Finally, define the keyword element using a complex type in which the type assigned to the term attribute is conditional on the value of the dict attribute:

 <xsd:element name="keyword">
  <xsd:complexType>
   <xsd:sequence/>
   <xsd:attribute name="dict" type="my:color_dictionaries"/>
   <xsd:attribute name="term">
    <xsd:alt cond="../@dict='html40'"   type="html40_color"/>
    <xsd:alt cond="../@dict='browsers'" type="defacto_color"/>
    <xsd:alt cond="../@dict='x11'"      type="X11_color"/>
   </xsd:attribute>
  </xsd:complexType>
 </xsd:element>


A second SchemaPath solution would be essentially the same as the check clause solution: define keyword with a loose type, use an XPath expression to capture the condition "If dict = "`html40`", then term must be one of ... and if dict = "`browsers`" then term must be one of ... and if dict = "`x11`" then term must be one of ...", and if the condition is not met, then conditionally assign the type `xsd:error` to the keyword element.

(Solutions proposed by MSM)

Conditional Type