ENGLISHJAPANESE
<< リラックスのしかた/第一部: RELAX Core/STEP 7: elementRuleとhedgeRule再訪 >>

STEP 7: elementRuleとhedgeRule再訪


$Id: step7.sdoc 1.13 2000/08/26 08:37:11 murata Exp $
STEP 6までのelementRule, hedgeRuleの説明は,DTDを知っている人がすぐに理解できる範囲だけを扱っています.実際にはもっと一般化された考え方がRELAXには存在しています.

elementRuleとラベル

elementRuleは,label属性 を持つことができます.ここでは,まずlabel属性の目的を説明し,次に機構を説明します.

出現位置によって異なる内容モデル

同じ名前のタグであっても出現位置によって内容モデルを変えたいということがしばしばあります.たとえば,章の中にある段落,表の中の段落は,どれも段落には違いありません.しかし,段落の中に許されるものは微妙に異なります.たとえば,章にある段落は脚注を含んでもかまいませんが,表の中の段落は文字だけに制限したいかもしれません.

<!-- This example is legal. -->
<section>
  <para>This paragraph can contain footnotes <footnote>This 
        is a footnote</footnote>.</para>
</section>

<!-- This example is illegal. -->
<table>
  ...
  <para>This paragraph cannot contain a footnote <footnote>This 
        is an illegal footnote</footnote>.</para>
  ...
</table>

したがって,段落が章の中にあるときと,表の中にあるときとでは,下記の二つの内容モデルを使いわけたくなります.

<!-- Case 1: subordinate to <section> elements. -->
<!ELEMENT para (#PCDATA|footnote)*>

<!-- Case 2: subordinate to <table> elements. -->
<!ELEMENT para (#PCDATA)>

出現位置によって内容モデルを変えたいという例はHTMLにもあります.HTMLでは,a要素が,他のa要素の中に直接的または間接的に現れることを禁止しています.同様のことは,form要素にもあてはまります.formの中にformが現れることは禁止されています.

<!-- This example is illegal. -->
<a href="foo"><span><a href="bar">dmy</a></span></a>

<!-- This example is also illegal. -->
<form>
  ...
  <div>
    <form>
      ...
    </form>
  </div>
  ...
</form>

HTMLでは,a要素はspan要素を含むことができます.入れ子は間接的であっても禁止したいので,aの外にあるspanではaを許し,中にあるspanでは許さないようにする必要があります.formについても同様で,formの外にあるdivではformを許し,中にあるdivでは許さないようにする必要があります.

<!-- Case 1: subordinate to <a> elements. -->
<!ELEMENT span (#PCDATA|a)*>

<!-- Case 2: not subordinate to <a> elements. -->
<!ELEMENT span (#PCDATA)>

しかし,DTDでは,タグ名が同じである限り,内容モデルは常に一つです.したがって,段落の出現箇所によって,内容モデルを変えることはできません.spanの内容モデルも常に一つであり,spanaの中にあるかどうかで内容モデルを変えることはできません.divの内容モデルも同様です.
この問題を回避するために,二つの方法が用いられてきました.一つの方法は,出現箇所ごとに別のタグ名を導入することです.この方法を用いた例を次に示します.章の中の段落にはparaInSectionというタグ名,表の中の段落はparaInTableというタグ名を導入しています.

<!ELEMENT paraInSection (#PCDATA|footnote)*>

<!ELEMENT paraInTable (#PCDATA)>

<!-- This example is legal. -->
<section>
  <paraInSection>This paragraph can contain footnotes <footnote>This 
        is a footnote</footnote>.</paraInSection>
</section>

<table>
  ...
  <paraInTable>This paragraph cannot contain a footnote.</paraInTable>
  ...
</table>

この方法には,DTDが大きくなると,ほとんど同じタグ名が急速に増えるという問題があります.段落,脚注,箇条書き等のような通常のタグ名が何倍にも増えるからです.
もう一つの方法は,タグ名は一つで済ませる代わりに,必要ないくつかの内容モデルをすべて足した内容モデルを作るというものです.この方法を用いた例を次に示します.章の中の段落だけではなく,表の中の段落にも,注釈が許されています.

<!ELEMENT para (#PCDATA|footnote)*>

この方法には,検証が不十分になるという問題があります.いまの例だと,以下の文書が検証を通ってしまいます.

<!-- This example is illegal. -->
<table>
  ...
  <para>This paragraph cannot contain a footnote <footnote>This 
        is an illegal footnote</footnote>.</para>
  ...
</table>

elementRuleのlabel属性

同じタグ名が出現位置によって異なる生け垣モデルを持てるようにするため,RELAXはラベルを導入しています.タグ名が同じであってもラベルが違えば,異なる生け垣モデルが適用されます.
elementRulelabel属性 を持つことができます.次に,elementRuleの基本的な形を示します.

<elementRule role="name" label="label">
  ...content model...
</elementRule>

label属性が省略された場合は,role属性と同じ値を指定したものと解釈されます.したがって,次の二つのelementRuleは等価です.

<elementRule role="foo">
  ...content model...
</elementRule>

<elementRule role="foo" label="foo">
  ...content model...
</elementRule>

脚注を含む段落と含まない段落とでラベルを使い分け,異なる内容モデルを指定した例を次に示します.

<elementRule role="para" label="paraWithFNotes">
  <mixed>
    <ref label="footnote" occurs="*"/>
  </mixed>
</elementRule>

<elementRule role="para" label="paraWithoutFNotes">
  <mixed>
    <empty/>
  <mixed/>
</elementRule>

<tag name="para"/>

一番目のelementRuleは,ラベルがparaWithFNotesである段落の内容は脚注を含んだ文字列であることを示しています.二番目のelementRuleは,ラベルがparaWithoutFNotesである段落の内容は単なる文字列であることを示しています.
多くの場合にラベルとタグ名は一対一に対応します.実際,STEP 6までの例ではそうでした.しかし,前節で示したような問題を扱うには,タグ名と一対一に対応しないラベルが必要になります.

ref要素のlabel属性

次に,ref要素のlabel属性を説明します.この属性の値は,常にラベルです.STEP 1では,値が要素型名だと書きましたが,あの説明は正確ではありません.RELAXには要素型という概念はありません.(実はXML 1.0にもありません.要素型宣言は定義されていますが,要素型の定義はどこを捜してもありません.)
前節の例にあるparaWithFNotesparaWithoutFNotesはラベルですから,ref要素から参照することができます.章のための内容モデルからは,paraWithFNotesを参照します.表(正確には表のセル)のための内容モデルからは,paraWithoutFNotesを参照します.

<elementRule role="section">
  <ref label="paraWithFNotes" occurs="*"/>
</elementRule>

<elementRule role="cell">
  <ref label="paraWithoutFNotes" occurs="*"/>
</elementRule>

ラベルの共有

ラベルを共有する複数のhedgeRule

複数のhedgeRulelabel属性に同一のラベルを指定しても構いません.次の例では,ラベルblockElemに対してhedgeRuleが二つ存在しています.

<hedgeRule label="blockElem">
  <ref label="para"/>
</hedgeRule>

<hedgeRule label="blockElem">
  <ref label="itemizedList"/>
</hedgeRule>

次のelementRuleは,このblockElemを参照しています.

<elementRule role="doc">
  <sequence>
    <ref label="title"/>
    <hedgeRef label="blockElem" occurs="*"/>
  </sequence>
</elementRule>

RELAX文法との照合のとき,hedgeRefは最初にすべて展開されます.このhedgeRefを例に展開の仕方を説明します.
ラベルblockElemを記述する二つのhedgeRuleの生け垣モデルは,どちらもref要素です.それらをまとめてchoice要素で括った結果は次のようになります.

<choice>
  <ref label="para"/>
  <ref label="itemizedList"/>
</choice>

展開したいhedgeRefは,occurs属性として*を指定しています.このchoiceに,これを転記します.

<choice occurs="*">
  <ref label="para"/>
  <ref label="itemizedList"/>
</choice>

最後に,このchoice要素でhedgeRefを置き換えます.

<elementRule role="doc">
  <sequence>
    <ref label="title"/>
    <choice occurs="*">
      <ref label="para"/>
      <ref label="itemizedList"/>
    </choice>
  </sequence>
</elementRule>

あるラベルを参照するhedgeRefを展開するための手順をまとめておきます.

  1. このラベルを記述するhedgeRuleをすべて探す.
  2. これらのhedgeRuleの生け垣モデルをchoice要素で一つに括る.
  3. hedgeRefoccurs属性をこのchoice要素に転記する.
  4. このchoiceで,hedgeRefを置き換える.
複数のhedgeRuleによるラベルの共有が認められているので,無理に一つのhedgeRuleにまとめる必要がありません.たとえば,numberedItemizedListblockElemの一種として追加しても,次のhedgeRuleを書き足すだけで済みます.他のhedgeRuleを変更する必要はありません.

<hedgeRule label="blockElem">
  <ref label="numberedItemizedList"/>
</hedgeRule>

hedgeRuleとelementRuleによるラベル共有の禁止

hedgeRuleelementRuleが同じラベルを共有することは許されません.たとえば,次の例は構文エラーです.

<hedgeRule label="foo">
  <ref label="bar"/>
</hedgeRule>

<elementRule role="foo" label="foo">
  <empty/>
</elementRule>

ラベルを共有する複数のelementRule

複数のelementRulelabel属性に同一のラベルを指定しても構いません.また,複数のelementRulerole属性が同一であっても構いません.
以下の例では,二つのelementRulerole属性としてsectionを指定しています.どちらもlabel属性は省略されていますから,sectionを指定しているものとみなされます.

<tag name="section"/>

<elementRule role="section">
  <ref label="para" occurs="*"/>
</elementRule>

<elementRule role="section">
  <choice occurs="*">
    <ref label="para"/>
    <ref label="fig"/>
  </choice>
</elementRule>

ラベルを共有する複数のelementRuleが存在するときは,どれか一つが成立するだけで十分です.
次のsection要素を考えます.一番目のelementRuleも二番目のelementRuleも,これを認めています.したがって,ラベルsectionを振ることができます.

<section><para/></section>

次のsection要素はfig要素を含んでいるので,二番目のelementRuleだけが成立しています.どれか一つが成立していれば十分ですから,やはりラベルsectionを振ることができます.

<section><para/><fig/><para/></section>

複数のelementRuleが同一のラベルを記述することの利点を説明します.すでにモジュールが作成されているとします.より広い範囲の文書が合法になるようにこのモジュールを変更することを考えます.
従来の方法では,既存のelementRuleを変更する必要があります.今まで合法だった文書が,この変更後にも合法であるという保証はありません.
RELAXでは,既存のelementRuleを変更せず,新たにelementRuleを追加することで対処できます.この方法だと,今まで合法だった文書がやはり合法であることは原理的に保証されています.
先の例では,paraだけをsectionの内容として許すのが当初の予定であり,一番目のelementRuleはそのために書かれています.その後に,figも内容として許すように二番目のelementRuleを追加しています.一番目のelementRuleは依然として有効ですから,今まで合法だった文書が合法でなくなることはありません.

まとめ

いままで大規模なDTDを書くのに苦労してきた人には,STEP 7は魅力的に映ると思います.必ず問題になる部分がRELAXでは綺麗に書けます.RELAX!

<< リラックスのしかた/第一部: RELAX Core/STEP 7: elementRuleとhedgeRule再訪 >>