Powered by SmartDoc

リラックスのしかた

$Id: howToRELAX.sdoc 1.9 2000/08/26 03:12:38 murata Exp $
村田 真

目次

第一部: RELAX Core

STEP 0: はじめに

$Id: step0.sdoc 1.10 2000/08/06 08:45:41 murata Exp $

STEP 0は,RELAXについて大まかに説明し,このチュートリアルの読み方を説明します.

RELAXの概要

RELAXは,XMLベースの言語を記述するための仕様です.たとえば,XHTML 1.0はXMLベースの言語ですから,RELAXで記述することができます.RELAXによる記述をRELAX文法といいます.

DTDとの比較

従来用いられてきたDTD(Document Type Definition)と比べて,RELAXは次のような特徴を備えています.

RELAXプロセッサ

RELAXプロセッサによって,XML文書をRELAX文法と照合することができます.RELAXプロセッサへの入力は,XML文書とRELAX文法です.正確には,RELAXプロセッサはXML文書やRELAX文法を直接扱うのではなく,XMLプロセッサがこれらを処理したあとの出力を扱います.

XML文書がこのRELAX文法に照らして合法かどうかを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!

STEP 1: XML DTDからの移行(パラメタ実体なし)

$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>

以下の章では,この例で示した構文を説明していきます.

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です.

interface要素

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要素によって表現されます.elementRulerole属性は,要素型の名前を指定します.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は,後述するchoicesequenceの中で使用することができます.このように拡張する理由は,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要素の子供は要素生け垣モデルです.choiceoccurs属性を持つことが出来ます.

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要素の子供は要素生け垣モデルです.sequenceoccurs属性を持つことが出来ます.

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で明らかになります.

データ型参照

elementRuletype 属性によって,データ型を参照する内容モデルを書くことができます.文書中に書かれた文字列は,参照されたデータ型と照合されます.指定できるのは,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要素は,foo1foo2の0個以上の繰り返しとマッチします.mixedの効果により,これらの要素の間に任意の文字が許されます.したがって,XMLの混在内容モデル(#PCDATA | foo1| foo2)*と等価になります.

生け垣モデル(#PCDATA)を表現するには,二つの方法があります.一つは,データ型stringtype属性で指定する方法です.もう一つは,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/>Mako&#x74;&#x6F;<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!

STEP 2: XML DTDからの移行(パラメタ実体あり)

$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を使うのではなく,hedgeRefmixedで括ってelementRuleの中に書きます.その例を次に示します.混在生け垣モデルはphraseを参照しており,phrasehedgeRuleで記述されています.

<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>

hedgeRefhedgeRule 順番

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の構造を変更できます.

noneの用途

同じくSTEP 1で述べたnonehedgeRuleの中で使います.以下に,使用例を示します.

<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の中からも同様に可能です.

refattPoolの順番

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>

複数のref要素

一つの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!

STEP 3: データ型

$Id: step3.sdoc 1.10 2000/08/26 03:14:38 murata Exp $

STEP 3では,データ型について説明します.

XML Schema Part 2のデータ型

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独自のデータ型

RELAXで独自に導入したデータ型はnoneemptyStringの二つです.

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への参照です.要素minInclusivemaxInclusiveはそれぞれ最小値と最大値に関する追加条件を表しています.したがって,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に子要素を追加します.

以下の例では,employeesex属性は,manwomanのどちらかであると指定しています.ここで,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!

STEP 4: 注釈

$Id: step4.sdoc 1.12 2000/11/01 13:43:29 murata Exp $

DTDを説明する資料を作るのは大変重要な作業です.DTDは単に構文を定義するだけですから,自然言語による大量の注釈によって意味を説明することが必要になります.XMLにあるコメントは使えますが,RELAXモジュールを構文解析してから表示するブラウザはコメントを無視してしまいます.

STEP 4は,モジュールに注釈を入れるための機構について説明します.これらは要素と属性を用いていますから,RELAXモジュールを構文解析するブラウザも,ユーザに分かりやすい形で注釈を表示することが出来ます.

annotation要素

注釈を挿入するためのトップレベルの要素が,annotation要素です.annotation要素が入れられるのは,次の箇所です.

elementRule要素の最初の子として,注釈を使用した例を以下に示します.注釈の内容は省略しています.

<elementRule role="para">
  <annotation> ... </annotation>
  <mixed>
    <ref label="fnote" occurs="*"/>
  </mixed>
</elementRule>

annotation要素は,子要素としてdocumentationappinfoをいくつでも持つことができます.

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!

STEP 5: 大きすぎるモジュールの分割

$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に関するelementRuletag要素があります.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に関するelementRuletagが記述されています.モジュールの最後にある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!

STEP 6: デフォルト値,実体,記法

$Id: step6.sdoc 1.9 2000/11/01 13:45:32 murata Exp $

DTDにある機能のうち,まだ扱っていないのは,デフォルト値,実体,記法です.STEP 6では,これらをどう扱うかを示します.

RELAXで導入しない理由

デフォルト値,実体,記法はRELAXにはありません.これらをRELAXで導入しないのは,既存のXMLパーサを使い続けるためです.

もし,RELAXでこれらの機能を導入したとします.たとえば,default属性をattribute要素に導入して,属性のデフォルトを指定できるようにします.しかし,既存のパーサはXML文書の解析時にRELAXモジュールをいっさい見てくれません.したがって,default属性もとうぜん使いません.実体や記法を宣言するための構文をRELAXに導入しても,やはり同じことです.

これらの機能をRELAXで導入するには,RELAX専用のXMLパーサを新たに作るしか方法はありません.RELAXに基づいてXML文書を作るユーザは,もちろんこのRELAX専用XMLパーサを使う必要があります.それだけでなく,このようにして作成されたXML文書を受け取るユーザにも,RELAX専用XMLパーサに乗り換えてもらうことが必要になります.これはあまり現実的ではありません.

DTDとRELAXの併用

では,デフォルト値,実体,記法はまったく使えないのでしょうか.いいえ,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にふさわしい方法です.

内部解析対象実体については,使ってかまいません."<"のように,内部解析対象実体(例えば&lt;)を用いて表現するのがもっとも自然なものもあります.

なお,DTDにデフォルト値,実体,記法を記述しても,期待と異なる結果になることがあります.これは,XMLパーサによっては外部DTDサブセットまたは外部パラメタ実体にある宣言を読まないためです.ここで示した例は,すべて内部サブセットで宣言しているので,予想外の結果になることはありません.

まとめ

STEP 6までで,DTDから乗り換えるには十分以上の機能があります.この範囲なら,DTDと相互に変換しても,データ型に関する情報以外は欠落しません.将来,XML Schemaに変換することも容易に可能でしょう.RELAX!

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>

elementRulelabel属性

同じタグ名が出現位置によって異なる生け垣モデルを持てるようにするため,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>

hedgeRuleelementRuleによるラベル共有の禁止

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!

STEP 8: tagとattPool再訪

$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属性の値がintegerstringかで,次の二つの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-stringval-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を持つことはありません.

  1. RELAXでは,宣言されていない属性があっても,役割が成立す ることにしています.こうしている理由は二つあります.一つは,宣言されて いない属性があっても処理を続行するというのが従来のXMLパーサの挙動であ ることです.もう一つは,HTMLでは宣言されていない属性を許していることで す.

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を参照しています.役割infoattPool要素によって規定されています.したがって,この例は構文エラーです.

<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要素です.これまでは,役割とタグ名は常に同じでしたが,必ずしもそうではありません.多くの場合にはラベルと役割とタグ名には一対一に対応しますが,一般的にはそうではありません.

タグ名,ラベル,役割を記述・参照する構文が何であるかを表にまとめておきます.

構文要素 タグ名/ラベル/役割の区別
elementRulerole属性 tagで記述された役割への参照
elementRulelabel属性 ラベルの記述
hedgeRulelabel属性 ラベルの記述
reflabel属性 elementRuleによって記述されたラベルへの参照
hedgeReflabel属性 hedgeRuleによって記述されたラベルへの参照
tagname属性 タグ名の記述
tagrole属性 役割の記述
attPoolrole属性 役割の記述
refrole属性 attPoolによって記述された役割への参照

次に,タグ名,ラベル,役割がXML文書で出現するかどうかを表にまとめておきます.

名前の種類 インスタンス中で RELAXモジュール中で
タグ名 出現する 節の一部として出現する
役割 出現しない 節の一部として出現する(役割の記述と参照)
ラベル 出現しない 生成規則の一部として出現する(ラベルの記述と参照)

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

STEP 9: 生け垣内容モデルelement

RELAXでは,elementという要素を生け垣モデルとして許しています.これは単なる略記法であり,refelementRuletagに展開されます.この節では,まずelement要素の目的を説明し,次に機構を説明します.

プログラミング言語やデータベース言語の模倣

RELAXはDTDの拡張であり,文法的なデータモデルを持っています.これは,プログラミング言語やデータベースのデータモデルとは異質なものです.しかし,RELAXでプログラミング言語の宣言やデータベース言語のスキーマを真似ることは考えておく必要があります.

プログラミング言語では,変数を宣言するときに同時にその型を宣言します.次の例では,変数xyを宣言し,型としてintを指定しています.

public class Point {
    int x;
    int y;
}

変数xが別のクラスで宣言されるときは,異なる型を指定されているかも知れません.次の例では,float型がクラスFooxに対して指定されています.

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要素によって置き換えられます.また,elementRuletagがそれぞれに対して生成されます.生成されるelementRuleの生け垣モデルは,elementtype属性で指定されたデータ型への参照です.

<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属性が付与されます.たとえば,最初のelementRuleelementoccurs="?"が指定されたとします(下記参照).

<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属性として指定されます.生成されたtagname属性としては,元々のelement要素のname属性の値が指定されます.

まとめ

プログラミング言語やデータベースの人たちは,element要素による記述は,とても自然で分かりやすいと思います.RELAX!

STEP 10: elementRuleへのtagの埋め込み

$Id: step10.sdoc 1.8 2000/11/01 13:46:38 murata Exp $

この節では,tag要素のelementRule要素への埋め込みについて説明します.

属性と生け垣モデルの同時記述

STEP 0から9まででは,属性やタグ名と,生け垣モデルは分離して記述されました.属性やタグ名はtagattPoolに,生け垣モデルはelementRulehedgeRuleに記述されます.elementRuleは,役割を通じてtagを参照し,このtagがさらにattPoolを参照します.

あるelementRuleとあるtagが密接に関連している場合には,両者を分離するのではなく,一つの要素にまとめてしまうほうが便利なこともあります.

elementRuletagとに分離した記述の例として,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属性は必須です.

埋め込まれたtagrole属性をもつことも許されません.この例では省略されていますが,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"を指定します.

最後に,elementRulerole属性を追加し,先に生成した役割を指定します.

<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!