$Id: step0.sdoc 1.10 2000/08/06 08:45:41 murata Exp $
STEP 0は,RELAXについて大まかに説明し,このチュートリアルの読み方を説明します.
RELAXは,XMLベースの言語を記述するための仕様です.たとえば,XHTML 1.0はXMLベースの言語ですから,RELAXで記述することができます.RELAXによる記述をRELAX文法といいます.
従来用いられてきたDTD(Document Type Definition)と比べて,RELAXは次のような特徴を備えています.
RELAXプロセッサによって,XML文書をRELAX文法と照合することができます.RELAXプロセッサへの入力は,XML文書とRELAX文法です.正確には,RELAXプロセッサはXML文書やRELAX文法を直接扱うのではなく,XMLプロセッサがこれらを処理したあとの出力を扱います.
XML文書がこのRELAX文法に照らして合法かどうかをRELAXプロセッサは報告します.RELAXプロセッサは,他にも診断メッセージを出すことがあります.RELAXプロセッサには,これ以外の出力はありません.
RELAXは,RELAX CoreとRELAX Namespaceの二つから成ります.RELAX Coreは,一つの名前空間にある要素とその属性を扱います.RELAX Coreは,XML Schema Part 2からデータ型を借用しています.RELAX Namespaceは,RELAX Coreで書かれた複数のモジュールを組み合わせ,複数の名前空間を扱います.現時点では,このチュートリアルはRELAX Coreだけを説明しています.
このチュートリアルは出来るだけ簡単に書かれています.このSTEPは例外ですが,他ではふんだんに例を用い,できるだけ具体的に説明しています.
STEP 1から10までは,RELAX Coreを扱います.どのSTEPまで読んでも,それなりの理解が得られます.STEP 1だけを読んでRELAXを使い始めることは十分可能です.STEP 6まで読めば,DTDでできる範囲の機能はすべて修得できます.すべてのRELAXプロセッサは,この範囲の機能をサポートします.STEP 7から10はRELAXの独自の機能を説明します.ただし,これらの機能を実装しないRELAXプロセッサも認められています.
説明を簡単にするため,STEP 1, 2にはわざと不正確な説明をしている点がいくつかあります.正確な説明は,STEP 7, 8にあります.
RELAXはとても簡単です.いままでDTDを使っていた人ならすぐに使えますし,使っていなかった人にもすぐ修得できます.ぜひRELAXを使ってみてください.RELAX!
$Id: step1.sdoc 1.13 2000/08/06 08:47:44 murata Exp $
STEP 1は,DTDをちょっと知っている人なら,すぐに移行できる範囲の機能です.また,DTD2RELAXコンバータが生成する範囲でもあります.
RELAXの感じをつかむため,一つのDTDをRELAXのモジュールとして表現してみます.
まずDTDを示します.title要素のnumber属性は整数だけに制限したいのですが,DTDでは出来ません.
<!ELEMENT doc (title, para*)> <!ELEMENT para (#PCDATA | em)*> <!ELEMENT title (#PCDATA | em)*> <!ELEMENT em (#PCDATA)> <!ATTLIST para class NMTOKEN #IMPLIED > <!ATTLIST title class NMTOKEN #IMPLIED number CDATA #REQUIRED >
つぎにRELAXモジュールを示します.number属性は整数であると指定されています.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="doc"/>
</interface>
<elementRule role="doc">
<sequence>
<ref label="title"/>
<ref label="para" occurs="*"/>
</sequence>
</elementRule>
<elementRule role="para">
<mixed>
<ref label="em" occurs="*"/>
</mixed>
</elementRule>
<elementRule role="title">
<mixed>
<ref label="em" occurs="*"/>
</mixed>
</elementRule>
<elementRule role="em" type="string"/>
<tag name="doc"/>
<tag name="para">
<attribute name="class" type="NMTOKEN"/>
</tag>
<tag name="title">
<attribute name="class" type="NMTOKEN"/>
<attribute name="number" required="true" type="integer"/>
</tag>
<tag name="em"/>
</module>
以下の章では,この例で示した構文を説明していきます.
RELAX文法は,いくつかのモジュールを組み合わせたものです.名前空間が一つだけで,それほど大規模でない文法なら,一つのモジュールがそのままRELAX文法になります.モジュールはmodule要素によって表現されます.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
...
</module>
moduleVersion 属性は,このモジュールのバージョンを表します.この例では,"1.2"です.
relaxCoreVersion 属性は,RELAX Core自体のバージョンを表します.現在は必ず"1.0"です.
targetNamespace 属性は,このモジュールが扱う名前空間を指定します.この例では,""を指定しています.
RELAX Coreのための名前空間名は,http://www.xml.gr.jp/xmlns/relaxCoreです.
moduleの先頭はinterface要素が入ります.一つのモジュールにはinterface要素が一つだけ存在します.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
...
</interface>
...
</module>
export要素interface要素の中にはexport要素がいくつか入ります.
<export label="foo"/>
export 要素のlabel 属性
の値は,ルートになりうる要素型です.export要素は複数あっても構いません.
以下の例は,要素型fooとbarがルートになりうることを表しています.
<interface> <export label="foo"/> <export label="bar"/> </interface>
XMLでいう要素型宣言(<!ELEMENT ...>)は,elementRule要素によって表現されます.elementRuleのrole属性は,要素型の名前を指定します.elementRuleは,interfaceの後にいくつでも書くことができます.
<elementRule role="element-type-name"> ...hedge model... </elementRule>
elemementRule要素は,生け垣モデルを持つことができます.生け垣とは,要素(子孫要素も含む)と文字データの並びのことです.生け垣モデルとは,どんな生け垣が許されるかを表す制約条件です.
生け垣モデルには,要素生け垣モデル,データ型参照,混在生け垣モデルがあります.
要素生け垣モデルは,empty, ref, choice, sequence, none要素とoccurs属性によって表現されます.要素生け垣モデルは,子供要素の並びとして何を許容するかを表現します.また,これらの要素の間に適当に空白を挿入することができます.
empty要素empty 要素は,空の内容を表します.
次のelementRuleを考えます.
<elementRule role="foo"> <empty/> </elementRule>
このelementRuleは,foo要素の内容が空であることを表しています.開始タグの直後に終了タグを置いても,空要素タグを用いても構いません.
<foo/>
<foo></foo>
XMLのEMPTYとは異なり,開始タグと終了タグの間に空白だけを置くことも許されます.
<foo> </foo>
emptyは,後述するchoiceやsequenceの中で使用することができます.このように拡張する理由は,STEP 2で明らかになります.なお,XMLのEMPTYとまったく同じ機能が必要なら,STEP 3にあるデータ型emptyStringを使って下さい.
以下では,要素型foo, foo1, foo2は,emptyを生け垣モデルとして持つelementRuleによって宣言されているものとみなして説明します.
ref要素ref 要素は,要素型を参照します.例えば,<ref label="foo"/>は,fooという要素型を参照します.
次のelementRuleを考えます.
<elementRule role="bar"> <ref label="foo"/> </elementRule>
このelementRuleは,bar要素の内容はfoo要素であることを示しています.例えば,次のbar要素はこのelementRuleに従っています.
<bar><foo/></bar>
foo要素の前後に空白が入っても構いません.
<bar> <foo/> </bar>
ref要素は,occurs 属性を持つことができます.値は,"*", "+", "?"のどれかで,それぞれ0回以上の繰り返し,1回以上の繰り返し,0回または1回の繰り返しを意味します.
occurs属性に"?"を指定した例を示します.
<elementRule role="bar"> <ref label="foo" occurs="?"/> </elementRule>
このelementRuleは,bar要素の内容はfoo要素または空であることを示しています.
<bar><foo/></bar>
<bar></bar>
foo要素の前後に空白が入っても構いません.また,空の場合にも空白を入れることができます.
<bar> <foo/> </bar>
<bar> </bar>
choice要素choice 要素は選択(XMLにある"|")を表します.choice要素の子供は要素生け垣モデルです.choiceもoccurs属性を持つことが出来ます.
choiceを用いたelementRuleの例を示します.
<elementRule role="bar">
<choice occurs="+">
<ref label="foo1"/>
<ref label="foo2"/>
</choice>
</elementRule>
このelementRuleは,bar要素の内容がfoo1要素またはfoo2要素の1回以上の繰り返しであることを示しています.
<bar><foo2/></bar>
<bar> <foo2/> </bar>
<bar> <foo1/> <foo2/> <foo1/> </bar>
sequence要素sequence 要素は並び(XMLにある",")を表します.sequence要素の子供は要素生け垣モデルです.sequenceもoccurs属性を持つことが出来ます.
sequenceを用いたelementRuleの例を示します.
<elementRule role="bar">
<sequence occurs="?">
<ref label="foo1"/>
<ref label="foo2"/>
</sequence>
</elementRule>
このelementRuleは,bar要素の内容が,foo1要素とfoo2要素の並び,または空であることを示しています.
<bar><foo1/><foo2/></bar>
<bar> <foo1/> <foo2/></bar>
<bar/>
<bar></bar>
<bar> </bar>
none要素none 要素は,RELAXではじめて導入されたもので,何ともマッチしないという生け垣モデルです.
<elementRule role="bar"> <none/> </elementRule>
このelementRuleは,barの内容としてはなにも許されないことを示しています.noneを導入する理由は,STEP 2で明らかになります.
elementRuleのtype 属性によって,データ型を参照する内容モデルを書くことができます.文書中に書かれた文字列は,参照されたデータ型と照合されます.指定できるのは,XML Schema Part 2で導入されたデータ型の名前か,RELAXが独自に導入したデータ型の名前です.データ型についてはSTEP 3で説明します.
typeを用いたelementRuleの例を示します.
<elementRule role="bar" type="integer"/>
このelementRuleは,bar要素の内容は整数を表現する文字列であることを示します.
<bar>10</bar>
前後に空白を挿入することはできません.すなわち,次の例は許されません.
<bar> 10 </bar>
mixed 要素は,XMLにある混在生け垣モデル(#PCDATA|a|b|...|z)*を大幅に拡張したものです.
<mixed>と</mixed>の間には,要素生け垣モデルが書けます.要素生け垣モデルを直接書いたときは,要素の間には空白だけが挿入できたことを思い出して下さい.mixedで囲むことにより,空白だけではなく,どんな文字でも挿入できるようになります.
XMLの(#PCDATA | foo1| foo2)*を表現するには,次のように書きます.
<elementRule role="bar">
<mixed>
<choice occurs="*">
<ref label="foo1"/>
<ref label="foo2"/>
</choice>
</mixed>
</elementRule>
このmixed要素に含まれるchoice要素は,foo1とfoo2の0個以上の繰り返しとマッチします.mixedの効果により,これらの要素の間に任意の文字が許されます.したがって,XMLの混在内容モデル(#PCDATA | foo1|
foo2)*と等価になります.
生け垣モデル(#PCDATA)を表現するには,二つの方法があります.一つは,データ型stringをtype属性で指定する方法です.もう一つは,mixed要素のなかに,空のラベル列だけとマッチする要素生け垣モデルを記述する方法です.次にその例を示します.
<elementRule role="bar" type="string"/>
<elementRule role="bar">
<mixed>
<empty/>
</mixed>
</elementRule>
より進んだ例として,次のelementRuleを考えます.
<elementRule role="bar">
<mixed>
<sequence>
<ref label="foo1"/>
<ref label="foo2"/>
</sequence>
</mixed>
</elementRule>
<foo1/>と<foo2/>の並びは,mixedの中のsequence要素にマッチします.したがって,次の例はこのelementRuleにより許されています.
<bar>Murata<foo1/>Makoto<foo2/>IUJ</bar>
次の例のようにCDATAセクションや文字参照が現れても構いません.
<bar><![CDATA[Murata]]><foo1/>Makoto<foo2/>IUJ</bar>
XMLでいう属性リスト宣言(<!ATTLIST ...>)は,tag要素によって表現されます.
<tag name="element-type-name"> ...list of attribute declarations... </tag>
tag要素は,子供要素としてattribute要素をいくつか持つことができます.
<tag name="element-type-name"> <attribute ... /> <attribute ... /> </tag>
一つのattribute要素は,一つの属性を宣言します.attribute要素の例を下に示します.
<attribute name="age" required="true" type="integer"/>
name 属性の値は,宣言される属性名です.この例ではageです.
required 属性の値としてtrueが指定されれば,この属性は省略できません.required属性が指定されなければ省略できます.この例では指定されていますから省略できません.
type 属性は,データ型名を指定します.type属性が省略された場合は,stringというデータ型(任意の文字列)が指定されたものとみなされます.
このattribute要素だけからなるtagの例を考えます.
<tag name="bar"> <attribute name="age" required="true" type="integer"/> </tag>
次の開始タグはこのtagに従っています.
<bar age="39">
次の二つの開始タグは従っていません.一つ目の例はage属性を省いていますし,二つ目の例は値が整数ではありません.
<bar>
<bar age="bu huo">
<!-- "bu huo" means forty years in Chinese. In Japan,
it is pronounced as "FUWAKU". -->
DTDでは,属性を持たない要素型については,属性リスト宣言を書く必要がありませんでした.しかし,RELAXでは,属性を持たない場合も空のtagを書く必要があります.たとえば要素型barが属性を持たない場合は次のように書きます.
<tag name="bar"/>
ここまで読んでいただければ,ただちにRELAXを使いはじめることができます.不便を感じなければ,STEP 2以降の内容を読む必要はありません.ぜひ使ってみて下さい.RELAX!
$Id: step2.sdoc 1.10 2000/11/01 13:41:12 murata Exp $
STEP 2では,同じことを何度も繰り返して記述する代わりに,一回だけ記述しておいたものを繰り返して参照するための機構が加わります.XMLにあるパラメタ実体に相当します.
生け垣モデルを一回だけ記述し,それを繰り返して参照するための機構がhedgeRule 要素です.DTDにあるパラメタ実体のうち,内容モデルで使われるものに相当します.
hedgeRule要素の構文を次に示します.fooはパラメタ実体の名前です.
<hedgeRule label="foo"> ...element content model... </hedgeRule>
このように定義したhedgeRuleを参照するには,<hedgeRef label="foo"/>と書きます.このhedgeRef要素は,hedgeRuleの中で指定された要素生け垣モデルで置き換えられます.
以下の例では,要素型docのためのelementRuleの生け垣モデルからhedgeRuleを参照しています.このelementRuleは,STEP 1の先頭にあるモジュールにあったものを書き換えて,title以外の部分をhedgeRuleで記述したものです.
<hedgeRule label="doc.body">
<ref label="para" occurs="*"/>
</hedgeRule>
<elementRule role="doc">
<sequence>
<ref label="title"/>
<hedgeRef label="doc.body"/>
</sequence>
</elementRule>
doc.bodyへの参照は次のように展開されます.
<elementRule role="doc">
<sequence>
<ref label="title"/>
<ref label="para" occurs="*"/>
</sequence>
</elementRule>
この例では,elementRuleの中からhedgeRuleを参照しましたが,hedgeRuleの中からも同様に可能です.
hedgeRuleの中には,要素生け垣モデルだけが書けます.データ型参照や混在生け垣モデルは許されません.たとえば,以下の二つはどれも許されません.
<hedgeRule label="mixed.param">
<mixed>
<choice occurs="*">
<ref label="em"/>
<ref label="strong"/>
<choice>
</mixed>
</hedgeRule>
<hedgeRule label="string.param" type="string"/>
hedgeRefと混在生け垣モデルを併用するときは,hedgeRuleの中でmixedを使うのではなく,hedgeRefをmixedで括ってelementRuleの中に書きます.その例を次に示します.混在生け垣モデルはphraseを参照しており,phraseはhedgeRuleで記述されています.
<hedgeRule label="phrase">
<choice>
<ref label="em"/>
<ref label="strong"/>
<choice>
</hedgeRule>
<elementRule role="p">
<mixed>
<hedgeRef label="phrase" occurs="*"/>
</mixed>
</elementRule>
occurs属性hedgeRef要素はoccurs属性を持つことができますし,hedgeRuleの中に書かれる要素生け垣モデルもoccurs属性を持つことができます.次の例では,両方にoccurs属性が指定されています.
<hedgeRule label="bar">
<sequence occurs="+" >
<ref label="foo1"/>
<ref label="foo2"/>
</sequence>
</hedgeRule>
<elementRule role="foo">
<hedgeRef label="bar" occurs="*"/>
</elementRule>
この例をDTDで表現すれば,パラメタ実体の展開がどう行われるべきかは明らかです.
<!ENTITY % bar "(foo1, foo2)+"> <!-- original --> <!ELEMENT foo (%bar;)*> <!-- expanded --> <!ELEMENT foo ((foo1, foo2)+)*>
上の例を展開した結果を次に示します.展開のときに,一つしか子供を持たないchoice要素が導入されていることに注意して下さい.これは,hedgeRef要素にあったoccurs="*"を引き継いでいます.
<elementRule role="foo">
<choice occurs="*">
<sequence occurs="+" >
<ref label="foo1"/>
<ref label="foo2"/>
</sequence>
</choice>
</elementRule>
hedgeRefとhedgeRuleの
順番DTDにあるパラメタ実体と違い,hedgeRefで参照する前にhedgeRuleを書く必要はありません.たとえば,次の記述はエラーではありません.
<elementRule role="doc">
<sequence>
<ref label="title"/>
<hedgeRef label="doc.body"/>
</sequence>
</elementRule>
<hedgeRule label="doc.body">
<ref label="para" occurs="*"/>
</hedgeRule>
自分自身を直接または間接に参照するようなhedgeRuleを書いてはいけません.次の例では,barのための生け垣モデルの中でbarを参照していますからエラーになります.
<hedgeRule label="bar">
<choice>
<ref label="title"/>
<hedgeRef label="bar" occurs="*"/>
</choice>
</hedgeRule>
次の例では,bar1のための生け垣モデルの中でbar2を参照しており,bar2のための生け垣モデルの中でbar1を参照しています.したがって,エラーになります.
<hedgeRule label="bar1">
<hedgeRef label="bar2" occurs="*"/>
</hedgeRule>
<hedgeRule label="bar2">
<choice>
<ref label="title"/>
<hedgeRef label="bar1"/>
</choice>
</hedgeRule>
emptyの用途STEP 1で述べたemptyは,主にhedgeRuleの中で使います.以下に例を示します.
<hedgeRule label="frontMatter">
<empty/>
</hedgeRule>
<elementRule role="section">
<sequence>
<ref label="title"/>
<hedgeRef label="frontMatter"/>
<ref label="para" occurs="*"/>
</sequence>
</elementRule>
このモジュールを再利用する人は,frontMatterの記述をカスタマイズすることによって,sectionの構造を変更できます.
同じくSTEP 1で述べたnoneもhedgeRuleの中で使います.以下に,使用例を示します.
<hedgeRule label="local-block-class">
<none/>
</hedgeRule>
<hedgeRule label="block-class">
<choice>
<ref label="para"/>
<ref label="fig"/>
<hedgeRef label="local-black-class"/>
</choice>
</hedgeRule>
このモジュールを再利用する人は,local-block-classの記述をカスタマイズすることによって,block-classの内容を変更できます.
属性宣言をいくつかまとめて一回だけ記述し,それを繰り返して参照するための機構がattPool要素です.DTDにあるパラメタ実体のうち,属性リスト宣言で使われるものに相当します.
attPool要素の構文を下に示します.fooはパラメタ実体の名前です.
<attPool role="foo"> ...attribute definitions... </attPool>
このように定義したattPoolを参照するには,属性定義の並びの先頭に<ref role="foo"/> と書きます.このref要素は,attPoolの中で指定された属性定義の並びで置き換えられます.
以下の例では,要素型titleのためのtag要素からattPoolを参照しています.このtagは,STEP 1の先頭にあるモジュールにあったものを書き直したものです.多くの要素型に共通するrole属性が,common.attというattPoolに記述されています.
<attPool role="common.att"> <attribute name="class" type="NMTOKEN"/> </attPool> <tag name="title"> <ref role="common.att"/> <attribute name="number" required="true" type="integer"/> </tag>
ref要素は次のように展開されます.
<tag name="title"> <attribute name="class" type="NMTOKEN"/> <attribute name="number" required="true" type="integer"/> </tag>
この例では,tagの中からattPoolを参照しましたが,attPoolの中からも同様に可能です.
refとattPoolの順番refで参照する前にattPoolを書く必要はありません.たとえば,次の記述はエラーではありません.
<tag name="title"> <ref role="common.att"/> <attribute name="number" required="true" type="integer"/> </tag> <attPool role="common.att"> <attribute name="role" type="NMTOKEN"/> </attPool>
一つのtagまたはattPoolの中に,複数のref要素を書くことができます.以下に,一つのattPoolのなかで複数のref要素を用いた例を示します.必須の属性をcommon-req.attにまとめ,必須ではない属性をcommon-opt.attにまとめています.この二つをcommon.attを記述するattPoolから参照しています.
<attPool role="common.att"> <ref role="common-req.att"/> <ref role="common-opt.att"/> </attPool> <attPool role="common-req.att"> <attribute name="role" type="NMTOKEN" required="true"/> </attPool> <attPool role="common-opt.att"> <attribute name="id" type="NMTOKEN"/> </attPool>
hedgeRuleのときと同様に,自分自身を直接的または間接的に参照するのはエラーです.たとえば,次の例はエラーです.
<attPool role="bar1"> <ref role="bar2"/> <attribute name="id" type="NMTOKEN"/> </attPool> <attPool role="bar2"> <ref role="bar1"/> </attPool>
STEP 2までで,XMLのDTDで出来ることはだいたい出来ます.ぜひ使ってみて下さい.RELAX!
$Id: step3.sdoc 1.10 2000/08/26 03:14:38 murata Exp $
STEP 3では,データ型について説明します.
XML Schema Part 2は,多くの組み込み済みデータ型を導入しています.それらは,XML Schema以外の仕様からも利用できるように配慮されています.RELAXは,これらの組み込み済みデータ型をすべて引き継いでいます.
XML Schema Part 2の組み込み済みデータ型には,XMLのDTDから借りたものと,新規に導入されたものがあります.次に,XMLのDTDから借りたものの一覧を示します.
次に,XML Schema Part 2で新たに導入した組み込み済みデータ型の一覧を示します.
XML Schema Part 2では,これらのデータ型を指定するとき,範囲などの制限をつけることができます.RELAXでも同様です.ただし,XML Schema Part 2とは異なり,ユーザがデータ型を定義することは出来ません.
RELAXで独自に導入したデータ型はnoneとemptyStringの二つです.
none空のデータ型です.どんな文字列もこのデータ型に属することはありません.RELAXでは,noneを用いて属性の禁止を指定します.次の例では,classという属性が禁止されています.noneを導入する理由は,STEP 8で明らかになります.
<tag name="p"> <attribute name="class" type="none"/> </tag>
したがって,次の開始タグは許されません.
<p class="foo">
emptyString空の文字列からなるデータ型です.DTDのEMPTYとの互換性があります.
<elementRule role="em" type="emptyString"/>
このelementRuleは,次の二つに限って許しています.<em>と</em>の間に空白が入ることは許されません.
<em/>
<em></em>
XML Schema Part 2と同様に,RELAXでもデータ型に制限を加えることができます.たとえば,integerに「15以上,65以下」という制限を加えることができます.制限を示す構文も,XML Schema Part 2と同様です.
elementRuleの場合elementRuleが参照しているデータ型に制限を加えるには,elementRuleに子要素を追加します.
以下の例では,要素型ageの生け垣モデルはintegerへの参照です.要素minInclusiveとmaxInclusiveはそれぞれ最小値と最大値に関する追加条件を表しています.したがって,age要素の内容は,18から65までの整数を表す文字列になります.
<elementRule role="age" type="integer"> <minInclusive value="18"/> <maxInclusive value="65"/> </elementRule>
age要素に,文字列"20"を内容として含むことができます.
<age>20</age>
しかし,文字列"11"は駄目です.
<age>11</age>
attributeの場合attributeが参照しているデータ型に制限を加えるには,attributeに子要素を追加します.
以下の例では,employeeのsex属性は,manかwomanのどちらかであると指定しています.ここで,enumerationは許される値を指定する付加条件です.
<tag name="employee">
<attribute name="sex" type="NMTOKEN">
<enumeration value="man"/>
<enumeration value="woman"/>
</attribute>
</tag>
sex属性は文字列"man"を持つことが出来ます.
<employee sex="man"/>
しかし,文字列"foo"は駄目です.
<employee sex="foo"/>
STEP 3まででもRELAXは十分使いでがあると思います.RELAX!
$Id: step4.sdoc 1.12 2000/11/01 13:43:29 murata Exp $
DTDを説明する資料を作るのは大変重要な作業です.DTDは単に構文を定義するだけですから,自然言語による大量の注釈によって意味を説明することが必要になります.XMLにあるコメントは使えますが,RELAXモジュールを構文解析してから表示するブラウザはコメントを無視してしまいます.
STEP 4は,モジュールに注釈を入れるための機構について説明します.これらは要素と属性を用いていますから,RELAXモジュールを構文解析するブラウザも,ユーザに分かりやすい形で注釈を表示することが出来ます.
annotation要素注釈を挿入するためのトップレベルの要素が,annotation要素です.annotation要素が入れられるのは,次の箇所です.
interface要素の前に一個だけ
export要素の中に一個だけ
elementRule要素の最初の子として一個だけ
hedgeRule要素の最初の子として一個だけ
tag要素の最初の子として一個だけ
attPool要素の最初の子として一個だけ
attribute要素の最初の子として一個だけ
include要素の子要素として一個だけ
element要素の最初の子として一個だけ
div要素の最初の子として一個だけ
elementRule要素の最初の子として,注釈を使用した例を以下に示します.注釈の内容は省略しています.
<elementRule role="para">
<annotation> ... </annotation>
<mixed>
<ref label="fnote" occurs="*"/>
</mixed>
</elementRule>
annotation要素は,子要素としてdocumentationとappinfoをいくつでも持つことができます.
documentation要素自然言語による説明を表現するための要素がdocumentation要素です.RELAX Namespaceが制定されていない現時点は,テキストデータしか入れられません.
さきに示した例にdocumentationを付加したものを次に示します.
<elementRule role="para">
<annotation>
<documentation>This is a paragraph.</documentation>
</annotation>
<mixed>
<ref label="fnote" occurs="*"/>
</mixed>
</elementRule>
documentation要素がsource属性を持つ場合は,説明を参照するURIが属性値です.この場合は,documentation要素の内容は使われません.モジュールを表示するツールは,リンクを利用した表示を提供します.
<elementRule role="para">
<annotation>
<documentation source="http://www.xml.gr.jp/relax/"/>
</annotation>
<mixed>
<ref label="fnote" occurs="*"/>
</mixed>
</elementRule>
documentation要素にxml:lang属性が指定されたときは,documentation要素の内容が,どの自然言語で書かれているかを示します.
つぎの例では,xml:langの値として"en"が指定されています.
<elementRule role="para">
<annotation>
<documentation xml:lang="en">This is a paragraph.</documentation>
</annotation>
<mixed>
<ref label="fnote" occurs="*"/>
</mixed>
</elementRule>
appinfo要素文書とRELAXモジュールを照合する検証プログラム以外にも,RELAXモジュールを操作するプログラムはいくらでも存在し得ます.たとえば,モジュールからデータベースのスキーマを生成するプログラムです.そのようなプログラムが利用するための隠し情報を表現するのがappinfo要素です.RELAX Namespaceが制定されていない現時点は,テキストデータしか入れられません.
<elementRule role="foo" type="integer"> <annotation><appinfo>default:1</appinfo></annotation> </elementRule>
appinfo要素がsource属性を持つ場合は,隠し情報を参照するURLが属性値です.この場合は,appinfo要素の内容は使われません.
div要素複数のelementRule, hedgeRule, tag, attPoolを一つにまとめて注釈をつけたいことがあります.そのために用意されているのがdiv要素です.
div要素は,module要素の中に,elementRule, hedgeRule, tag, attPoolと同レベルに置きます.div要素の中にさらにdiv要素を置くこともできます.div要素の中には,elementRule, hedgeRule, tag,
attPool, divを置くことができます.
STEP 1に示したモジュールにdivを使用して注釈を加えたものを下に示します.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="doc"/>
</interface>
<div>
<annotation>
<documentation>The root node</documentation>
</annotation>
<elementRule role="doc">
<sequence>
<ref label="title"/>
<ref label="para" occurs="*"/>
</sequence>
</elementRule>
<tag name="doc"/>
</div>
<div>
<annotation>
<documentation>Paragraphs</documentation>
</annotation>
<elementRule role="para">
<mixed>
<ref label="em" occurs="*"/>
</mixed>
</elementRule>
<tag name="para">
<attribute name="class" type="NMTOKEN"/>
</tag>
</div>
<elementRule role="title">
<mixed>
<ref label="em" occurs="*"/>
</mixed>
</elementRule>
<tag name="title">
<attribute name="class" type="NMTOKEN"/>
<attribute name="number" required="true" type="integer"/>
</tag>
<elementRule role="em" type="string"/>
<tag name="em"/>
</module>
STEP 4で,モジュールの説明を書くのが容易になりました.RELAX!
$Id: step5.sdoc 1.7 2000/04/14 12:40:02 murata Exp $
大きすぎるモジュールは管理するのが大変です.STEP 5は,モジュールをいくつかのファイルに分割して管理するための機能を説明します.
200の要素型をもつDTDでRELAXで表現すると仮定します.なお,この程度の大きさのDTDはいくつも存在します.RELAXでは,各要素型ごとにelementRuleとtagがそれぞれ必要とします.一つが平均3行としても1200行になります.文書化を積極的に行えば3000行ぐらいの長さになることも考えられます.これは,一つのファイルに格納するにはいささか長すぎます.
DTDのときは,外部パラメタ実体を用いて,大規模なDTDを分割して管理していました.RELAXでも,大規模なモジュールを分割するための機構が必要になります.
include要素RELAXでは,他のモジュールをinclude要素によって参照することができます.include要素は参照されたモジュールの本体によって置き換えられます.
include要素の使用例を考えてみます.まず,インクルードされる側のモジュールをつぎに示します.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface/>
<elementRule role="bar" type="emptyString"/>
<tag name="bar"/>
</module>
このモジュールには,barに関するelementRuleとtag要素があります.interface要素は空要素です.このモジュールはbar.rlxに格納されているものとします.
次に,このモジュールを参照して取り込む側のモジュールを示します.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="foo"/>
</interface>
<elementRule role="foo">
<ref label="bar"/>
</elementRule>
<tag name="foo"/>
<include moduleLocation="bar.rlx" />
</module>
このモジュールには,fooに関するelementRuleとtagが記述されています.モジュールの最後にあるinclude要素は,moduleLocation属性でbar.rlxを指定しています.
include要素は,参照されているモジュールの本体(module要素の内容のうち,interface以降)によって置き換えられます.この例では,つぎのように置き換えられます.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="foo"/>
</interface>
<elementRule role="foo">
<ref label="bar"/>
</elementRule>
<tag name="foo"/>
<elementRule role="bar" type="emptyString"/>
<tag name="bar"/>
</module>
interfaceが空でない場合前節の例では,参照される側のモジュールのinterface要素は空でした.次のように,interface要素に子要素を追加してみます.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="bar"/>
</interface>
<elementRule role="bar" type="emptyString"/>
<tag name="bar"/>
</module>
この場合には,インクルードされる側のモジュールにあるinterface要素の内容が,インクルードする側のモジュールのinterface要素に追加されます.この例では次のようになります.
<module
moduleVersion="1.2"
relaxCoreVersion="1.0"
targetNamespace=""
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="foo"/>
<export label="bar"/>
</interface>
<elementRule role="foo">
<ref label="bar"/>
</elementRule>
<tag name="foo"/>
<elementRule role="bar" type="emptyString"/>
<tag name="bar"/>
</module>
STEP 5で,大規模なモジュールも管理できるようになりました.RELAX!
$Id: step6.sdoc 1.9 2000/11/01 13:45:32 murata Exp $
DTDにある機能のうち,まだ扱っていないのは,デフォルト値,実体,記法です.STEP 6では,これらをどう扱うかを示します.
デフォルト値,実体,記法はRELAXにはありません.これらをRELAXで導入しないのは,既存のXMLパーサを使い続けるためです.
もし,RELAXでこれらの機能を導入したとします.たとえば,default属性をattribute要素に導入して,属性のデフォルトを指定できるようにします.しかし,既存のパーサはXML文書の解析時にRELAXモジュールをいっさい見てくれません.したがって,default属性もとうぜん使いません.実体や記法を宣言するための構文をRELAXに導入しても,やはり同じことです.
これらの機能をRELAXで導入するには,RELAX専用のXMLパーサを新たに作るしか方法はありません.RELAXに基づいてXML文書を作るユーザは,もちろんこのRELAX専用XMLパーサを使う必要があります.それだけでなく,このようにして作成されたXML文書を受け取るユーザにも,RELAX専用XMLパーサに乗り換えてもらうことが必要になります.これはあまり現実的ではありません.
では,デフォルト値,実体,記法はまったく使えないのでしょうか.いいえ,RELAXとDTDを併用すれば,これらの機能を使うことができます.
以下に,DTDを持つXML文書を示します.
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE person [ <!ATTLIST person bloodType CDATA "A"> ]> <person/>
この文書と照合するRELAXモジュールを示します.
<module
moduleVersion="1.0"
relaxCoreVersion="1.0"
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="person"/>
</interface>
<elementRule role="person">
<empty/>
</elementRule>
<tag name="person">
<attribute name="bloodType">
<enumeration value="O"/>
<enumeration value="A"/>
<enumeration value="B"/>
<enumeration value="AB"/>
</attribute>
</tag>
</module>
この例では,DTDの側でデフォルト値"A"を指定しています.XMLパーサは,これを利用してくれます.RELAXモジュールに照らしてこのXML文書を検証することも問題なく可能です."A"が属性値として指定された場合とまったく同様にして検証は行われます.
同様に,実体や記法もDTDで記述することができます.まず,解析対象実体の例を示します.
<?xml version="1.0"?> <!DOCTYPE doc [ <!ENTITY foo "This is a pen"> ]> <doc> <para>&foo;</para> </doc>
この文書は,次のRELAXモジュールに従っています.
<module
moduleVersion="1.0"
relaxCoreVersion="1.0"
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="doc"/>
</interface>
<elementRule role="doc">
<ref label="para" occurs="*"/>
</elementRule>
<elementRule role="para" type="string"/>
<tag name="doc"/>
<tag name="para"/>
</module>
つぎに,解析対象外実体と記法を用いた例を示します.
<?xml version="1.0"?> <!DOCTYPE doc [ <!NOTATION eps PUBLIC "-//ISBN 0-7923-9432-1::Graphic Notation//NOTATION Adobe Systems Encapsulated Postscript//EN"> <!ENTITY logo_eps SYSTEM "logo.eps" NDATA eps> <!ELEMENT doc EMPTY> <!ATTLIST doc logo ENTITY #IMPLIED> ]> <doc logo="logo_eps"/>
この文書も,次のRELAXモジュールに従っています.
<module
moduleVersion="1.0"
relaxCoreVersion="1.0"
xmlns="http://www.xml.gr.jp/xmlns/relaxCore">
<interface>
<export label="doc"/>
</interface>
<elementRule role="doc" type="emptyString"/>
<tag name="doc">
<attribute name="logo" type="ENTITY"/>
</tag>
</module>
前節で示したように,DTDとRELAXを併用すれば,デフォルト値,実体,記法を使うことは可能です.しかし,やはり使わないに越したことはありません.
デフォルト値については,アプリケーションプログラムの側で対応することができます.属性が省略されたときの処理として記述すればいいわけです.属性が省略されたとき,あらかじめ決めた値を追加するようなXSLTスクリプトを書くこともできます.
外部解析対象実体と解析対象外実体については,リンクを使いましょう.リンクのほうがWWWにふさわしい方法です.
内部解析対象実体については,使ってかまいません."<"のように,内部解析対象実体(例えば<)を用いて表現するのがもっとも自然なものもあります.
なお,DTDにデフォルト値,実体,記法を記述しても,期待と異なる結果になることがあります.これは,XMLパーサによっては外部DTDサブセットまたは外部パラメタ実体にある宣言を読まないためです.ここで示した例は,すべて内部サブセットで宣言しているので,予想外の結果になることはありません.
STEP 6までで,DTDから乗り換えるには十分以上の機能があります.この範囲なら,DTDと相互に変換しても,データ型に関する情報以外は欠落しません.将来,XML Schemaに変換することも容易に可能でしょう.RELAX!
$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の内容モデルも常に一つであり,spanがaの中にあるかどうかで内容モデルを変えることはできません.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はラベルを導入しています.タグ名が同じであってもラベルが違えば,異なる生け垣モデルが適用されます.
elementRuleはlabel属性
を持つことができます.次に,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にもありません.要素型宣言は定義されていますが,要素型の定義はどこを捜してもありません.)
前節の例にあるparaWithFNotesとparaWithoutFNotesはラベルですから,ref要素から参照することができます.章のための内容モデルからは,paraWithFNotesを参照します.表(正確には表のセル)のための内容モデルからは,paraWithoutFNotesを参照します.
<elementRule role="section"> <ref label="paraWithFNotes" occurs="*"/> </elementRule> <elementRule role="cell"> <ref label="paraWithoutFNotes" occurs="*"/> </elementRule>
hedgeRule複数のhedgeRuleがlabel属性に同一のラベルを指定しても構いません.次の例では,ラベル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を展開するための手順をまとめておきます.
hedgeRuleをすべて探す.
hedgeRuleの生け垣モデルをchoice要素で一つに括る.
hedgeRefのoccurs属性をこのchoice要素に転記する.
choiceで,hedgeRefを置き換える.
複数のhedgeRuleによるラベルの共有が認められているので,無理に一つのhedgeRuleにまとめる必要がありません.たとえば,numberedItemizedListをblockElemの一種として追加しても,次のhedgeRuleを書き足すだけで済みます.他のhedgeRuleを変更する必要はありません.
<hedgeRule label="blockElem"> <ref label="numberedItemizedList"/> </hedgeRule>
hedgeRuleとelementRuleによるラベル共有の禁止hedgeRuleとelementRuleが同じラベルを共有することは許されません.たとえば,次の例は構文エラーです.
<hedgeRule label="foo"> <ref label="bar"/> </hedgeRule> <elementRule role="foo" label="foo"> <empty/> </elementRule>
elementRule複数のelementRuleがlabel属性に同一のラベルを指定しても構いません.また,複数のelementRuleのrole属性が同一であっても構いません.
以下の例では,二つのelementRuleがrole属性として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!
$Id: step8.sdoc 1.15 2000/08/26 14:03:04 murata Exp murata $
STEP 2では,tagが属性リスト宣言に相当し,attPoolが属性だけを定義するパラメータ実体に相当すると説明しました.実際には,より一般的な考え方がRELAXでは存在しています.
tag要素のrole属性tag要素は,name属性のほかに,role属性を持つことができます.この節では,まずこの拡張の動機を説明し,つぎにrole属性を導入します.
同じ名前のタグであっても属性の値によって内容モデルを変えたいということがしばしばあります.たとえば,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>
したがって,type属性の値がintegerかstringかで,次の二つのelementRuleを使い分けたくなります.
<!-- Case 1: type="integer" --> <elementRule role="val" type="integer"/> <!-- Case 2: type="string" --> <elementRule role="val" type="string"/>
しかし,STEP 7までの機能では,タグ名に対して内容モデルを記述しています.属性値は使われません.したがって,type属性の値に関わらず,同じelementRuleが適用されてしまいます.
tag要素が表す制約条件tag要素は,name属性のほかに,role属性を持つことができます.tagの基本的な形は次の通りです.name属性はタグ名を指定しますが,role属性が指定するのは役割です.
<tag name="tag-name" role="role-name"> ... </tag>
tag要素は,タグ名と属性に関する制約条件の集まりを役割に関連づけます.tag要素に記述された条件を開始タグ(または空要素タグ)が満たすとき,このタグは指定された役割を持つと言います.
例として,次のtag要素を考えます.
<tag name="val" role="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" role="val-string">
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="string"/>
</attribute>
</tag>
次の開始タグは役割val-integerは持ちませんが,役割val-stringは持ちます.
<val type="string">
tag要素によって指定されていない属性があっても構いません.たとえば,次の開始タグの属性unknownは,先のtag要素で言及されていません.しかし,この開始タグは,役割val-stringを持ちます.ただし,診断メッセージは出力されます.
<val type="string" unknown="">
STEP 1から7までにあるようなrole属性がないtag要素はどう解釈されるのでしょうか.role属性がない場合は,name属性と同じものが指定されたものとみなされます.したがって次の二つのtag要素は同じ意味を持ちます.
<tag name="foo"> <attribute name="bar" type="int"/> </tag> <tag name="foo" role="foo"> <attribute name="bar" type="int"/> </tag>
elementRule要素のrole属性elementRule要素のrole属性は,タグ名を指定するのではなく役割を指定します.したがって,タグ名が同じであっても属性が違えば,別の生け垣モデルを使うことができます.
先の例で示した役割val-stringとval-integerを使えば,タグ名がvalである開始タグに対して二つのelementRuleを使い分けることが出来ます.述語名val-stringを指定するelementRuleば,type属性の値がstringである開始タグを扱います.役割val-integerを指定すれば,type属性の値がintegerであるものを扱います.
<!-- Case 1: type="integer" -->
<tag name="val" role="val-integer">
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="integer"/>
</attribute>
</tag>
<elementRule role="val-integer" label="val" type="integer"/>
<!-- Case 2: type="string" -->
<tag name="val" role="val-string">
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="string"/>
</attribute>
</tag>
<elementRule role="val-string" label="val" type="string"/>
二つのtag要素がタグ名val"と属性名"typeを指定していることに注意して下さい.RELAXにおけるtag要素は,一度だけしか行われない宣言ではなく,何度でも書ける制約条件なのです.
ref要素による参照の禁止ref要素が参照する役割を,tag要素で記述してはいけません.記述するのなら,attPool要素で記述する必要があります.
次の例にあるref要素は,tag要素によって記述された役割fooを参照しています.したがって,構文エラーです.
<tag name="foo"/> <attPool role="bar"> <ref role="foo"/> </attPool>
none再訪STEP 3ではデータ型noneを導入しました.属性が指定された場合と指定されない場合とで異なる内容モデルを用いるときに,noneは有益です.
たとえば<div class="sec">と<div>で内容モデルを変えることを考えます.前者を表現する役割divSecは次のように記述されます.
<tag name="div" role="divSec">
<attribute name="class" type="string">
<enumeration value="sec"/>
</attribute>
</tag>
問題は<div>を表現する役割divWithoutClassをどう記述するかです.次のように記述したとします.
<tag name="div" role="divWithoutClass"/>
この記述では,<div class="sec">に対しても,divWithoutClassが成立してしまいます.宣言されていない属性があるというメッセージは出ますが,両方の役割を持つとみなされてしまいます.(1)
属性classを持たないということを陽に指定するには,データ型noneを用いて次のように書く必要があります.
<tag name="div" role="divWithoutClass"> <attribute name="class" type="none"/> </tag>
データ型noneに属する文字列は存在しないので,どんな値を属性classに指定しても役割divWithoutClassを持つことはありません.
attPool要素attPool要素は,パラメタ実体のように単に展開されるものではありません.attPool要素は,tag要素とほとんど同等の概念です.
attPoolが表す制約条件tag要素は,タグ名に関する制約条件と属性に関する制約条件の集まりに役割を関連付けるものでした.attPool要素もほとんど同じで,違いはタグ名に関する制約条件を指定できないことだけです.すなわち,属性に関する制限の集まりに役割を関連付けるものがattPool要素です.
つぎのattPoolを考えます.
<attPool role="info">
<attribute name="class" required="true">
<enumeration value="informative"/>
</attribute>
</attPool>
このattPool要素は,「classという属性が指定されており,その値はinformativeという文字列である」という条件にinfoという役割を関連付けています.タグ名が何であっても構いません.このattPoolによって,次の空要素タグは役割infoを持ちます.
<some class="informative"/>
tag要素のときと同様に,宣言されていない属性があっても構いません.たとえば,次の開始タグに対して役割infoは成立します.
<some class="informative" unknown=""/>
elementRule要素による参照の禁止elementRule要素のrole属性で指定する役割を,attPool要素で記述してはいけません.記述するのなら,tag要素で記述する必要があります.
次のelementRuleは役割infoを参照しています.役割infoはattPool要素によって規定されています.したがって,この例は構文エラーです.
<attPool role="info"/> <elementRule role="info" label="informative" type="emptyString"/>
tagまたはattPool要素による役割の共有の禁止複数のtagまたはattPool要素が,一つの役割を共有することはできません.
以下の例では,二つのtag要素が役割barを共有しています.したがって,構文エラーです.
<tag name="foo1" role="bar"> <attribute name="a" type="string"/> ... </tag> <tag name="foo2" role="bar"> <attribute name="b" type="string"/> ... </tag>
以下の例では,役割とタグ名の両方を共有しています.これも構文エラーです.
<tag name="foo" role="foo"> <attribute name="a" type="string"/> ... </tag> <tag name="foo" role="foo"> <attribute name="b" type="string"/> ... </tag>
role属性が省略され,name属性の値が使われたときも,共有は許されません.次の二つのtag要素は,上の二つのtag要素と同じ意味を持ちます.したがって,この例も構文エラーです.
<tag name="foo"> <attribute name="a" type="string"/> ... </tag> <tag name="foo"> <attribute name="b" type="string"/> ... </tag>
つぎの例では,二つのattPool要素が役割barを共有しています.したがって,構文エラーです.
<attPool role="bar"> <attribute name="a" type="string"/> ... </attPool> <attPool role="bar"> <attribute name="b" type="string"/> ... </attPool>
最後に,tag要素とattPool要素が役割barを共有した例を示します.これも,構文エラーです.
<attPool role="bar"> <attribute name="a" type="string"/> ... </attPool> <tag role="bar" name="foo"> <attribute name="b" type="string"/> ... </tag>
STEP 7までは,tag要素はタグ名と属性を宣言するものだと見なしてきました.本当は,タグ名に関する条件と属性に関する条件に役割を関連づけるのがtag要素です.これまでは,役割とタグ名は常に同じでしたが,必ずしもそうではありません.多くの場合にはラベルと役割とタグ名には一対一に対応しますが,一般的にはそうではありません.
タグ名,ラベル,役割を記述・参照する構文が何であるかを表にまとめておきます.
| 構文要素 | タグ名/ラベル/役割の区別 |
elementRuleのrole属性 |
tagで記述された役割への参照 |
elementRuleのlabel属性 |
ラベルの記述 |
hedgeRuleのlabel属性 |
ラベルの記述 |
refのlabel属性 |
elementRuleによって記述されたラベルへの参照 |
hedgeRefのlabel属性 |
hedgeRuleによって記述されたラベルへの参照 |
tagのname属性 |
タグ名の記述 |
tagのrole属性 |
役割の記述 |
attPoolのrole属性 |
役割の記述 |
refのrole属性 |
attPoolによって記述された役割への参照 |
次に,タグ名,ラベル,役割がXML文書で出現するかどうかを表にまとめておきます.
| 名前の種類 | インスタンス中で | RELAXモジュール中で |
| タグ名 | 出現する | 節の一部として出現する |
| 役割 | 出現しない | 節の一部として出現する(役割の記述と参照) |
| ラベル | 出現しない | 生成規則の一部として出現する(ラベルの記述と参照) |
属性の値によって内容モデルを変えることは,従来のDTDでは不可能でしたが,RELAXでは可能になりました.必要な拡張はrole属性だけです.RELAXの簡潔さ,強力さの現れといっていいでしょう.RELAX!
RELAXでは,elementという要素を生け垣モデルとして許しています.これは単なる略記法であり,refとelementRuleとtagに展開されます.この節では,まずelement要素の目的を説明し,次に機構を説明します.
RELAXはDTDの拡張であり,文法的なデータモデルを持っています.これは,プログラミング言語やデータベースのデータモデルとは異質なものです.しかし,RELAXでプログラミング言語の宣言やデータベース言語のスキーマを真似ることは考えておく必要があります.
プログラミング言語では,変数を宣言するときに同時にその型を宣言します.次の例では,変数xとyを宣言し,型としてintを指定しています.
public class Point {
int x;
int y;
}
変数xが別のクラスで宣言されるときは,異なる型を指定されているかも知れません.次の例では,float型がクラスFooのxに対して指定されています.
public class Foo {
float x;
}
element要素element要素は,変数名と型の指定を同時に行うための要素生け垣モデルです.element要素は,必ずname属性とtypeを持ちます.さらに,occurs属性も持つことができます.
<element name="tag-name" type="datatype-name"/>
<element name="tag-name" type="datatype-name" occurs="*"/>
element要素を用いることによって次のようなtag要素とelementRule要素が書けます.
<tag name="Point"/>
<elementRule role="Point">
<sequence>
<element name="x" type="integer"/>
<element name="y" type="integer"/>
</sequence>
</elementRule>
x=100かつy=200であるようなPointは次のXML文書によって表現できます.
<Point> <x>100</x> <y>200</y> </Point>
ref, elementRule, tagへの展開element要素は単なる構文糖衣(syntax sugar)です.生け垣モデル中のelement要素はref要素に置き換えられ,elementRule要素とtag要素が生成されます.
前節のelementRuleをもう一度示します.このelement要素がどう展開されるかを考えてみます.
<elementRule label="Point">
<sequence>
<element name="x" type="integer"/>
<element name="y" type="integer"/>
</sequence>
</elementRule>
どちらのelement要素もref要素によって置き換えられます.また,elementRuleとtagがそれぞれに対して生成されます.生成されるelementRuleの生け垣モデルは,elementのtype属性で指定されたデータ型への参照です.
<elementRule label="Point">
<sequence>
<ref label="Point$1"/>
<ref label="Point$2"/>
</sequence>
</elementRule>
<elementRule role="Point$1" label="Point$1" type="integer"/>
<tag role="Point$1" name="x"/>
<elementRule role="Point$2" label="Point$2" type="integer"/>
<tag role="Point$2" name="y"/>
element要素がoccurs属性を持っているときは,生成されるref要素にoccurs属性が付与されます.たとえば,最初のelementRuleでelementにoccurs="?"が指定されたとします(下記参照).
<elementRule label="Point">
<sequence>
<element name="x" type="integer" occurs="?"/>
<element name="y" type="integer" occurs="?"/>
</sequence>
</elementRule>
展開の結果は次のようになります.
<elementRule label="Point">
<sequence>
<ref label="Point$1" occurs="?"/>
<ref label="Point$2" occurs="?"/>
</sequence>
</elementRule>
<elementRule role="Point$1" label="Point$1" type="integer"/>
<tag role="Point$1" name="x"/>
<elementRule role="Point$2" label="Point$2" type="integer"/>
<tag role="Point$2" name="y"/>
ここではelement要素がどう展開されるかをまとめておきます.
ref要素の生成ref要素を生成します.そのlabel属性の値として,他のラベルと衝突しないラベルが自動生成されます.もし,element要素にoccurs属性があれば,生成されたref要素に引き継がれます.
elementRule要素の生成elementRuleを生成します.そのrole属性の値として,他の役割と衝突しない役割が生成されます.label属性の値は,ref要素を生成したときに自動生成したラベルです.このelementRuleの生け垣モデルとして,element要素のtype属性を転記します.
tag要素の生成tag要素を生成します.elementRuleを生成するときに自動生成した役割がrole属性として指定されます.生成されたtagのname属性としては,元々のelement要素のname属性の値が指定されます.
プログラミング言語やデータベースの人たちは,element要素による記述は,とても自然で分かりやすいと思います.RELAX!
$Id: step10.sdoc 1.8 2000/11/01 13:46:38 murata Exp $
この節では,tag要素のelementRule要素への埋め込みについて説明します.
STEP 0から9まででは,属性やタグ名と,生け垣モデルは分離して記述されました.属性やタグ名はtagとattPoolに,生け垣モデルはelementRuleとhedgeRuleに記述されます.elementRuleは,役割を通じてtagを参照し,このtagがさらにattPoolを参照します.
あるelementRuleとあるtagが密接に関連している場合には,両者を分離するのではなく,一つの要素にまとめてしまうほうが便利なこともあります.
elementRuleとtagとに分離した記述の例として,STEP 8にある例をもう一度示します.
<!-- Case 1: type="integer" -->
<tag name="val" role="val-integer">
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="integer"/>
</attribute>
</tag>
<elementRule role="val-integer" label="val" type="integer"/>
<!-- Case 2: type="string" -->
<tag name="val" role="val-string">
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="string"/>
</attribute>
</tag>
<elementRule role="val-string" label="val" type="string"/>
ここで,役割val-integer, val-stringがこの二つのelementRule要素からだけしか参照されていないとします.わざわざval-integer, val-stringという二つの名前をつけて参照するより,以下のように直接埋め込みたくなります.
<!-- Case 1: type="integer" -->
<elementRule label="val" type="integer">
<tag>
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="integer"/>
</attribute>
</tag>
</elementRule>
<!-- Case 2: type="string" -->
<elementRule label="val" type="string">
<tag>
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="string"/>
</attribute>
</tag>
</elementRule>
この書き方の利点は,役割に名をつけて参照する必要がないことです.書き直す前は,タグ名ともラベルとも違う名前をつけていました.この名前がなくなった分だけ,読むときの負担が減っています.
属性と生け垣モデルとを同時に記述できることを利点と感じる人もいます.たとえば,x座標とy座標を持つ点を,XMLで二通りに表現した例を以下に示します.前者では,属性によって表現し,後者では要素によって表現しています.両者の差異は僅かであり,書き換えが容易に可能です.
<elementRule label="point" type="emptyString">
<tag>
<attribute name="x" type="integer"/>
<attribute name="y" type="integer"/>
</tag>
</elementRule>
<elementRule label="point">
<tag/>
<sequence>
<element name="x" type="integer"/>
<element name="y" type="integer"/>
</sequence>
</elementRule>
tagを含むelementRuleは,role属性を持つことは許されません.その代わりに,label属性は必須です.
埋め込まれたtagがrole属性をもつことも許されません.この例では省略されていますが,name属性を持つことは許されています.
tagの処理埋め込まれたtagは,elementRuleから取り出され,その兄弟要素になります.本STEPの最初の例を用いて説明します.
<elementRule label="val" type="integer">
<tag>
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="integer"/>
</attribute>
</tag>
</elementRule>
まず,適当な役割を生成します.この例では,val$1という役割を生成しています.
次に,埋め込まれたtag要素をelementRuleから取り出し,その兄弟要素として配置します.role属性を追加し,先に生成した役割のval$1を属性値として指定します.
tag要素がname属性を持っていないときに限って,name属性を追加します.値としては,elementRule要素のlabel属性の値を用います.この例では,tag要素のname属性の値として"val"を指定します.
最後に,elementRuleにrole属性を追加し,先に生成した役割を指定します.
<elementRule label="val" type="integer" role="val$1">
</elementRule>
<tag name="val" role="val$1">
<attribute name="type" type="NMTOKEN" required="true">
<enumeration value="integer"/>
</attribute>
</tag>
要素と属性をまとめて記述したいときには,tagの埋め込みによって,簡潔で分かりやすい記述が可能になります.RELAX!