STEP 8: tagとattList再訪

$Id: step8.html 1.7 2000/02/21 13:15:18 murata Exp $

text by 村田 真

html by 難波 亮丞


STEP 2では、tagが属性リスト宣言に相当し、attListが属 性だけを定義するパラメータ実体に相当すると説明しました。実際には, より一般的な考え方がRELAXでは存在しています。

1. 述語とタグ名の違い

tag要素は、name属性のほかに、pred属性を持つことができます。 この節では、まずpred属性の目的を説明し、次に機構を説明します。

1.1 属性によって異なる内容モデル

同じ名前のタグであっても属性の値によって内容モデルを変えたいという ことがしばしばあります。たとえば、valという要素のtype属性の値に よってvalの内容モデルを変えたいとします。type="integer"と指定されていれば 内容モデルはデータ型integerへの参照ですし、type="string"と指定されて いればデータ型stringへの参照です。

<!-- This is legal. -->
<val type="integer">10</val>

<!-- This is also legal. -->
<val type="string">foo bar</val>

<!-- This is illegal. -->
<val type="integer">foo bar</val>

したがって、次の二つのelementRuleを使い分けたくなります。

<!-- Case 1: type="integer" -->
<elementRule pred="val" type="integer"/>

<!-- Case 2: type="string" -->
<elementRule pred="val" type="string"/>

しかし,STEP 8までの機能では,タグ名に対して内容モデル を記述しています。属性値は使われません.したがって、 type属性の値に関わらず,同じelementRuleが適用されてしまいます.

1.2 tag要素のpred属性

tag要素は、name属性のほかに、pred 属性を持つことができます。 tagの基本的な形は次の通りです。name属性はタグ名を指定しますが, pred属性が指定するのは述語名です.

<tag name="tag-name" pred="predicate-name">
  ...
</tag>

tag要素は,タグ名と属性に関する制約条件の集まりを述語 に関連づけます.tag要素に記述された条件を開始タグ(または空要素 タグ)が満たすとき,指定された述語がこのタグにおいて成り立つと言います.

例として,次のtag要素を考えます.

<tag name="val" pred="val-integer">
  <attribute name="type" type="NMTOKEN" required="true">
    <enumeration value="integer"/>
  </attribute>
</tag>

このtag要素は,「タグ名がvalであり,type属性が存在すればその値は "integer"という文字列である」という条件を表しています.この条件を満た す開始タグ(または空要素タグ)では,述語val-integerが成り立ちます. したがって,次の開始タグでは,述語val-integerが成り立ちます.

<val type="integer">

次のtag要素では,type属性の値に関する条件が 「値が"string"という文字列である」に変わり,述語名が val-stringに変わっています.

<tag name="val" pred="val-string">
  <attribute name="type" type="NMTOKEN" required="true">
    <enumeration value="string"/>
  </attribute>
</tag>

次の開始タグでは述語val-integerは成り立ちませんが, 述語val-stringは成り立ちます.

<val type="string">

言及されていない属性があっても構いません.たとえば,次の開始タグの 属性unknownは,先のtag要素で言及されていません.しかし,この開始タグに おいて述語val-stringは成り立ちます.

<val type="string" unknown="">

1.3 pred属性の省略

それでは,STEP 1から8までで見てきた,pred属性がないtag要素はどう 解釈されるのでしょうか.pred属性がない場合は、name属性と同じものが 指定されたものとみなされます。したがって次の二つのtag要素は同じ意味を持ち ます.

<tag name="foo">
  ...
</tag>

<tag name="foo" pred="foo">
  ...
</tag>

いままで,tag要素はタグ名と属性を宣言するものだと見なしてきました. 本当は,タグ名に関する条件と属性に関する条件に述語を関連づけるのが tag要素です.これまでは,述語名とタグ名がたまたま等しかっただ けに過ぎません.

STEP 7では、ラベルとタグ名とは違うことが分かりましたが、ここ では述語とタグ名の違いが分かったわけです。多くの場合にはラベル名 と述語名とタグ名は等しくなりますが、一般的にはそうではありません.

1.4 elementRuleのpred属性

elementRuleのpred属性は,タグ名を指定するのではなく述語名を 指定します.したがって,タグ名が同じであっても属性が違えば,別の内容モデル を使うことができます.

先の例で示した述語val-stringとval-integerを使えば,タグ名がvalであ る開始タグに対して二つのelementRule を使い分けることが出来ます.述語名 val-stringを指定すればtype属性の値が"string"である開始タグを参照します. 述語名val-integerを指定すればtype属性の値が"integer"であるものを参照し ます.

<!-- Case 1: type="integer" -->

<tag name="val" pred="val-integer">
  <attribute name="type" type="NMTOKEN" required="true">
    <enumeration value="integer"/>
  </attribute>
</tag>

<elementRule pred="val-integer" type="integer"/>

<!-- Case 2: type="string" -->

<tag name="val" pred="val-string">
  <attribute name="type" type="NMTOKEN" required="true">
    <enumeration value="string"/>
  </attribute>
</tag>

<elementRule pred="val-string" type="string"/>

この例で,二つのtag要素がタグ名"val"と属性名"type"を指定している ことに注意して下さい.RELAXにおけるtag要素は,一度だけしか行われない宣 言ではなく,何度でも書ける制約条件なのです.

2. 述語を共有するtag要素

RELAXでは複数のtag要素が,一つの述語を共有することができます. この節では,共有することの目的を説明し,次に機構を説明します.

2.1 属性間の依存関係

ひとつのタグの属性の間に依存関係があることがあります.もっとも 多いのは,二つの属性のうち一方だけが指定できるというものです.たとえば, HTMLのアンカー要素(A)は,name属性かhref属性のどちらか一方だけを持つことが できます.

すなわち,次の二つの開始タグは許されます.

<A href="#fragment">
<A name="dmy">

しかし,次の開始タグは許されません.

<A name="dmy" href="#fragment">

DTDではこのような制限は書けませんでした.どちらの属性も省略可能 と指定し,「どちらか一方だけを必ず指定すること」と自然言語で注意するしか ありませんでした.

2.2 述語名の共有

RELAXでは,同一の述語を共有する複数のtag要素によって,このような依 存関係を表現することが出来ます.各tag要素は,属性についてのいくつかの 条件を指定します.少なくとも一つのtag要素が表す条件を満たせば,こ の述語は成立します.

例えば,アンカー要素については次のように書きます.データ型noneは, どんな文字列も許容しない空のデータ型です.RELAXでは言及されていない属 性も許容されるので,noneを用いて明示的に禁止しています.

<tag name="A" pred="A">
  <attribute name="name" type="NMTOKEN"/>
  <attribute name="href" type="none"/>
</tag>

<tag name="A" pred="A">
  <attribute name="href" type="URI"/>
  <attribute name="name" type="none"/>
</tag>

前の節で示したように,pred属性を省略することも可能です.上の 二つのtag要素は,次の二つのtag要素と同じ意味を持ちます.

<tag name="A">
  <attribute name="name" type="NMTOKEN"/>
  <attribute name="href" type="none"/>
</tag>

<tag name="A">
  <attribute name="href" type="URI"/>
  <attribute name="name" type="none"/>
</tag>

タグ名がAである開始タグにname属性だけが指定されているなら,一番目の tag要素によって述語Aが成り立ちます.href属性だけが指定されているな ら,二番目のtag要素によって述語Aが成り立ちます.両方の属性が指定さ れている場合には,どちらのtag要素の条件も満たされませんから,述語 Aは成り立ちません.

次のelementRuleを考えます.pred属性は述語Aを参照しています.

<elementRule pred="A" type="string"/>

内容が文字列であるアンカー要素がname属性だけを持つなら,この elementRuleを用いて,ラベルAを振ることができます.href属性だけを持つと きも同様です.しかし,両方の属性を持っている場合は,述語Aが成り立たず, ラベルAを振ることも出来ません.

なお,XML 1.0の属性リスト宣言は、同じタグ名について複数あっても構いません。 それらは単に連結されます(同じ名前の属性については早いもの勝ち)。 言い換えると,XML 1.0の属性リスト宣言ではandで繋がった複数の条件です. RELAX ではorで繋がった複数の条件なのと対照的です.

3. attList要素

attList要素は、パラメタ実体のように単に展開されるものではありません。 attList要素は,tag要素とほとんど同等の概念です。

3.1 attListが表す制約条件

tag要素は、タグ名に関する制約条件と属性に関する制約条件の集まりに述 語を関連付けるものでした。attList要素もほとんど同じで,違いはタグ名に 関する制約条件を指定できないことだけです.すなわち、属性に関する制限の 集まりに述語を関連付けるものがattList要素です。

つぎのattListを考えます。

<attList pred="with-role">
  <attribute name="role" required="true">
    <enumeration value="informative"/>
  </attribute>
</attList>

このattList要素は、「roleという属性が指定されており、その値は informativeという文字列である」という条件にwith-roleという述語を 関連付けたものです。タグ名が何であっても構いません.次の空要素タグでは、 このattListによって、述語with-roleが成立します。

<some role="informative"/>

tag要素のときと同様に,宣言されていない属性があっても構いません. たとえば,次の開始タグに対して述語with-roleは成立します.

<some role="informative" unknown=""/>

3.2 elementRuleからattListへの参照

elementRuleのpred属性で指定する述語は、tag要素で記述されている ものとは限りません.attList要素で規述されている述語であっても 構いません.

次のelementRuleは述語としてwith-roleを指定しており、内容モデルはデー タ型emptyStringへの参照です。with-roleは前節で示したattList要素によって 規定されています.

<elementRule pred="with-role" label="informative" type="emptyString"/>

前節で示した空要素を次にもう一度示します.この空要素において述語 with-roleが成立していることは前節で確認しました.したがって,この elementRuleによってラベルinformativeに到達できます。

<some role="informative" unknown=""/>

3.3 refからtagへの参照

述語の中に現れるref要素は、attList要素で記述されているものとは 限りません.tag要素で規定されている記述であっても構いません.次の 例にあるtag要素は、P というタグ名をparagraphという述語に関連づけて います.

<tag pred="paragraph" name="P"/>

<attList pred="japanese_paragraph">
  <ref pred="paragraph/">
  <attrbute name="xml:lang" required="true" type="lang">
    <enumeration value="ja"/>
  </attribute>
</attList>

この例にあるattList要素は,ref要素によって述語paragraphを参照し ています.attListは,xml:lang属性の値がjaであるという条件を規定してい ます.したがって,タグ名がPで,xml:lang属性の値がjaである開始タグ(ま たは空要素タグ)は,述語japanese_paragraphにマッチします.

4. 述語を共有するtag要素とattList要素

同一の述語を共有するtag要素とattList要素がいくつあっても構いません.例えば,次の例はエラーではありません.

<module
      moduleVersion="1.2"
      relaxCoreVersion="1.0"
      targetNamespace=""
      xmlns="http://www.xml.gr.jp/2000/relaxCore">

  <interface>
    <export labels="foo1"/>
  </interface>

<elementRule pred="foo1" type="string"/>

<tag pred="foo1" name="bar"/>

<tag pred="foo1" name="bar">
  <attribute name="role" required="true">
    <enumeration value="informative"/>
  </attribute>
</tag>

<tag pred="foo2" name="bar"/>

<attList pred="foo1">
  <attribute name="role" required="true">
    <enumeration value="foo1"/>
  </attribute>
</attList>

<attList pred="foo2">
  <attribute name="role" required="true">
    <enumeration value="foo2"/>
  </attribute>
</attList>

ある開始タグ(または空要素タグ)で述語foo1が成り立つのは,最 初の二つのtag要素もしくは,下から二番目のattList要素のうち,どれか一つが 成立するときです.

<foo1 unknown="">
<foo1 >
<foo1>

同様に,述語foo2が成り立つのは,三番目のtag要素 もしくは最後のattList要素が成立するときです.

一つのタグにおいて複数の述語が成立することもあります.この例で, 最後のattList要素と先頭のtag要素の両方が成立すれば,一つのタグが 述語foo1とfoo2の両方とマッチすることになります.

5. タグ名,ラベル,述語を指定する構文

タグ名,ラベル,述語を指定する構文が何であるかを表にまとめておきます.

構文 タグ名/ラベル/述語
elementRuleのpred属性 述語名
elementRuleのlabel属性 ラベル
contentRuleのlabel属性 ラベル
refのlabel属性 ラベル
tagのname属性 タグ名
tagのpred属性 述語名
attListのpred属性 述語名
refのpred属性 述語名

6. 言及されていない属性

RELAXでは,tag要素やattList要素で言及されていない属性があっても構い ません.言及されていない属性については,単に警告が出されるだけです.そ の属性を無視して,検証自体はそのまま続行されます.

例として,次のモジュールを考えます.

<module
      moduleVersion="1.2"
      relaxCoreVersion="1.0"
      xmlns="http://www.xml.gr.jp/2000/relaxCore">

  <interface>
    <export labels="foo"/>
  </interface>

  <tag pred="foo">
    <ref pred="bar"/>
    <attribute name="a"/>
  </tag>

  <attList pred="bar">
    <attribute name="b"/>
  </attList>

  <attList pred="bar1">
    <attribute name="c"/>
  </attList>

  <elementRule pred="foo" type="emptyString"/>

<module>

次の要素を,このモジュールに対して検証したとします. RELAXプロセッサは,c属性が宣言されていないという 警告メッセージを出して処理を続行します.

<foo a="" b="" c=""/>

ある属性が宣言されていないかどうかは,elementRuleを決めるのに役立っ たtagまたはattListで宣言されているかどうかで決めます.役立った tagまたはattListの少なくとも一つで宣言されていれば,警告メッセージは 出ません.

たとえば,この例では,最初のtagはelementRuleを決めるのに役立ってい ます.このfoo要素は述語fooにマッチし,この述語を参照する elementRuleが存在するからです.また,一番目のattListもelementRuleを決 めるのに役立っています.このfoo要素は述語barにマッチし,このパター ンが最初のtagから参照されているからです.

いっぽう,二番目のattListはelementRuleを決めるのに役立っていません. 述語bar1にこのfoo要素はマッチしますが,bar1はelementRuleから参照さ れておらず,役立った述語(fooとbar)からも参照されていないからです.

最初のtagと一番目のattListでは属性aとbしか宣言されていません. したがって,属性cは宣言されていないとみなされ,警告メッセージが 出力されます.

もし,二番目のattList要素が述語barを参照するように変更された とします.

  <attList pred="bar">
    <attribute name="c"/>
  </attList>

この場合は,このattListも役立ちます.barは先頭のtagから参照されている 述語だからです.したがって,属性cも宣言されているとみなされます. 警告メッセージは出ません.

では,さらにこのattListを次のように変更した場合はどうでしょう.

  <attList pred="bar">
    <attribute name="c"/>
    <attribute name="d" required="true"/>
  </attList>

この場合は,属性cは宣言されているとはみなされません.この attListが要素fooに対して成立しない(必須の属性dが存在しない)からです. したがって,cは宣言されていないという警告メッセージが出されます.

7. まとめ

属性の値によって内容モデルを変えること,複数の属性のうち一方だけを 指定することは,従来のDTDでは不可能でした.RELAXではどちらも可能 になりました.必要な拡張はpred属性だけです.RELAXの簡潔さ,強力さ の現れといっていいでしょう.RELAX!

mura034@attglobal.net

Valid HTML 4.0!