平坦添加例外による内容モデルの拡張

檜山正幸 (HIYAMA Masayuki)
Tue Apr 12 2005:start
Tue Apr 12 2005:prefinal

目次

この記事は、月刊『JavaWorld』誌(IDGジャパン)連載「XMLボキャブラリ の理論と実践」第28回(原稿執筆は2003年9月)付属のコラム(本文外)を、 わずかに変更したものである。

1. 便利な機能? それともSGMLの亡霊

XMLは、SGMLの余分な機能を削り落として定義されたものである。名目上、 XMLはSGMLのサブセットであり、任意のXML文書はSGML文書ともみなせるはずで ある(ほんとのところはあやしいが)。今から見れば、記法(notation)や外 部実体参照なんてSGML機能は、残しても何の意味もなかった。DTDや内部実体 も、利便性よりは弊害が多かった。つまり、実はもっともっとスリム化できた のではないかと思うのである(所詮、これは後知恵というやつではあるが)。

その一方で、「削除せずにXMLでも残しておいてもよかったのじゃないか」と 思うSGML機能も少数ながらある。そのひとつはCONREF(content reference) 属性である。その発想は、「特定の属性でデータを参照してもよいし、その属 性がないなら内容をデータとして使う」というものである。具体例を出そう。 (X)HTMLの要素imgには、srcという属性があり、その属性値により画像リソー スを参照する。現状、srcがなかったらエラーとみなされるが、もし、imgの 内容に画像データを書いてもいいなら、けっこう便利そうだ。画像はバイナリ だから要素内容にならないと思うかもしれないが、SVGなら大丈夫だ。つまり、 <img src="figure.svg" /> と書いてもいいが、ファイルfigure.svgの中身を 次のようにimgの内容として展開してもよいことにする。

<img>
  <svg:svg xmlns:svg="http://www.w3.org/2000/svg"
     width="400" height="400">
   <svg:circle cx="200" cy="200" r="100" 
      style="fill:red; stroke:black; stroke-width:2" />
   ...(省略)...
  </svg:svg>
</img>

現実には、要素imgの内容にアニメーションやらイベントの定義を書く方法が 既に使われているから、「属性srcがなかったら内容を代わりに使う」方法を 採用するのは難しいかもしれない。だが、SGMLのCONREFというアイディアが捨 てたものではないことはわかるだろう。

他に、インスタンスには書いてないタグを自動的に挿入するSGML機能(マー クアップ最小化)も、たまに使いたくなる。実際に欲しい機能はタグの挿 入というよりは、むしろ要素の自動補完である。例えば、次の誤った XHTMLインスタンスに対して補完をして欲しくなる。

<html xmlns="http://www.w3.org/1999/xhtml">
 <title>これくらいいいでしょう</title>
 <body>
   ...(省略)...   
 </body>
</html>

XHTML仕様では、要素htmlの最初の子要素はheadでなくてはならない。だから いきなりtitleを書いてはいけないのだ。が、titleはheadのなかにしか出現し ない要素だから、処理系がtitleの出現を見たらbodyを補ってもいいだろう。 実際にMathMLでは、inferred elementというマークアップ最小化に似た機能が 採用されている。特定の状況下では、要素mrowが省略できて、処理系により推 測(infer)される。

NOTE: 2005-04-12追加

次の節の冒頭に書いてあるとおり、第1節はマクラ話である。だから、ここに 書いてあることをあまり深刻に受け止めてもらうと困る。僕は、CONREFやマー クアップ最小化の復活を切実に願っているわけではない。

CONREF機能は、設計手法や運用規則として導入することができるだろう (「XMLボキャブラリのデ ザイン・パターン」の「(10)属性または内容」を参照)。また、 inferred elementのような機能は、アプリケーションのレイヤーで実現すれば 事足りると思っている。

2. 「混ぜ方」の記述が曖昧だ

ここまでの話は実はマクラだ。ここからが本題なのだが、やはり、かつての SGMLにありXMLでは削除され、そして今では誰も見向きもしない機能を再生す る話である。

最初に問題意識を説明しよう。 XHTMLでは、ほとんどどこにでも他のボキャブラリの要素を混ぜてよいようで ある。例えばMathMLは、XHTMLに混ぜるのが一番多い使い方だろう。 一方、SVGではそうはいかない。勝手な場所に他のボキャブラリの要素 を入れられてもうまく表示できない。そのため、svg:foreignObjectという容 器となる要素を準備している。

さて、「混ぜてもよいようである」なんて曖昧な書き方をしたのは、XHTMLへ の他のボキャブラリの混ぜ方が明示されてないからである。XHTML仕様で は、抽象モジュールとか最小内容モデルとか導入しているが、筆者には理解でき ない。抽象モジュール/最小内容モデルを持ち出して話が厳密になったとは思 えない、かえってワケがわからない。もう少しワケがわかる方法で、混ぜ方を 記述できないだろうか。これが問題意識である。

ここで、なつかしの(?)SGML機能の登場である。それは、内容モデルに対する 例外というやつだ。内容モデル例外を(そのままではないが) 採用して、「混ぜ方」の記述を合理化したい。

3. 削除要素群と添加要素群

SGMLの内容モデル例外について説明しよう。説明のための記法として、DTD流 の正規表現とパラメータ実体参照を使う。おやっ? 筆者はDTDが嫌いなのでは なかったかって? 確かにDTDの使用を推奨してないが、正規表現の構文は嫌い ではない。手で書くのも楽で可読性に優れているから、説明には好都合だ。パ ラメータ実体はプレイスホルダーとして使う。

例からはじめる。XHTMLのアンカー要素の内容モデルが仮に(あくまで仮に!) (#PCDATA|%inlines;)* だったとしよう。 %inlines; はインライン要素 を並べたものである。アンカー要素a自体も %inlines; に含まれる。そうなる と実は具合が悪い。なぜなら、アンカー要素のネストが可能だが、それではブ ラウザが困ってしまう。そこで、次のような書き方をする。

<!ELEMENT a  (#PCDATA|%inlines;)* -(a) >

ここで出現した「-(a)」が、削除要素群による内容モデル例外である。これ は、アンカー要素の子孫としてアンカー要素の出現を禁止する働きがある。も うひとつ例を出そう。

<!ELEMENT strong  (#PCDATA|%inlines;)* -(em|strong) >
これは、要素strongの子孫に、emとstrongの出現を禁じている。

削除要素群とは逆に、いくつかの要素の出現を例外的に許可するのが、添加 要素群による内容モデル例外である。例えば、次の例では、要素bodyのどんな ところにでも要素page-breakを挿入できるようになる。

<!ELEMENT body  (%body-content;) +(page-break) >

4. 内容モデル例外の問題点と対処

削除(exclusion)要素群と添加(inclusion)要素群による内容モデル例外が、 XMLでは使用してないのには理由がある。例外があると、内容モデルの検証が 面倒になるし、(人間による)理解も困難になる。また、要素の定義を 再利用するときにも障害となる。

内容モデル例外の弊害は、例外指定(削除要素群、添加要素群)が、要素ツ リー全体に影響してしまうことから生じる。例外の適用範囲を、子孫全体では なくて子供だけに限定すれば、さほど問題は生じない。そこで、子供にだけ適用する 例外を平坦例外と呼ぶことにしよう(筆者の造語)。

平坦例外を表す記号として、「:-(要素群)」と「:+(要素群)」を採用しよう。 例えば、 (#PCDATA|%inlines;)* :-(em|strong) は、emとstrongが直接(第 1レベルで)出現することを禁止する。だが、ツリーのずっと下のほうにemやstrong が出現することまでは禁止しない。次の例では、bodyの直接の子としてなら、 part-separatorをどこにでも置けることを意味する。

<!ELEMENT body  (%body-content;) :+(part-separator) >

平坦例外は、正規表現を本質的に拡張するものではない。このことは、平坦 例外を使って書いた内容モデルは、平坦例外なしでも書けるということである。 ただし、そうすると記述が煩雑になる。つまり、平坦例外は正規表現記述の手 間を劇的に節約する手段になる。

5. 平坦添加例外とワイルドカードによる混ぜ方の記述

平坦添加例外を使うと、前もって定義された内容モデルに対して、挿入を許 す要素群を追加できる。その挿入可能要素として、他のボキャブラリの要素を 指定すれば、「他のボキャブラリの要素を子要素として任意に挿入可能」であ ることを簡潔に表現できる。例を出そう。

<!ELEMENT p (#PCDATA|%inlines;)* :+(#foreign) >
ここで、特殊記号#foreignは、他の名前空間に属する任意の要素を表すとす る。すると、要素pの内容に文字列と任意のインライン要素(前もって定義さ れている)、さらには他の名前空間に属する任意の要素を許すことになる。

#foreignは、XML Schemaなら<xs:any namespace="##other" />と書けるもの で、ワイルドカードである。名前空間の条件以外は、どんな要素ともマッチす るからワイルドカードと呼ぶ。

XML Schemaで定義されたワイルドカードはたいして表現力を持たないのだが、 名前付きのワイルドカード(メタ変数)や、もっと詳細な条件指定を導入すれ ば、「混ぜ方」の指定に使えるだろう。そしてそのとき、平坦添加例外を併用 すれば記述を簡略化できる。

ソフトウェアとマークアップを統合するフレームワークにおいて、コンポネ ントベースでボキャブラリの実装を行おうとすると、「混ぜ方」の精密な記述 はどうしても必要となる。その手法が整備されることを望む。

NOTE: 2005-04-12追加

「整備されることを望む」と言っているだけでは、どうもラチがあかないよ うである。「混ぜ方」の精密な記述は、Chimairaのなかで取り扱おうと予定し ている課題の1つである。