というか、XML 1.0 仕様書では システム識別子の相対 URI はその実体の在処を基準として解釈する
って明記されてるじゃないですか。というわけで、以下は「中途半端にパラメタ実体参照を展開すると混乱することもあるから気を付けましょう」くらいのつもりで読んで下さい。
ちょっとした疑問が生じたので、メモしておく。DTD 中のシステム識別子は URI の形式で記述されることが多いが、これを相対パスで記述した場合、そのパスはどこを基準としたものと見なされるのだろうか?
勿論、過去に W3C の公開している DTD を見る限りでは、「そのシステム識別子が(実際に実体宣言などとして)記述されているファイルのあるディレクトリ」を基準にしているようなのだが、考えてみるとこの規則は well-defined であるとは言えない気がする。
例として、次のようなファイル構成を考えてみよう (&directory;
の置換テキストが何になるか、ということを考えて貰いたい)。
http://foo.org/doc/doc.xml
<?xml version="1.0" ?>
<!DOCTYPE doc SYSTEM "http://foo.org/dtd/dtd.dtd" >
<doc>&directory;</doc>
http://foo.org/dtd/dtd.dtd
<!ELEMENT doc (#PCDATA) >
<!ENTITY % mod.mod SYSTEM "http://foo.org/mod/mod.mod" >
%mod.mod;
http://foo.org/mod/mod.mod
<!ENTITY directory.mod SYSTEM "directory.mod" >
%directory.mod;
http://foo.org/doc/directory.mod
<!ENTITY directory "doc" >
http://foo.org/dtd/directory.mod
<!ENTITY directory "dtd" >
http://foo.org/mod/directory.mod
<!ENTITY directory "mod" >
この場合、<!ENTITY directory.mod SYSTEM "directory.mod" >
という宣言そのものは http://foo.org/mod/mod.mod
に記述されているため、W3C 方式(?)での &directory;
の置換テキストは mod となる。この方式は、HTML や CSS などでの相対パスの扱いから類推されるものであるし、一見問題はなさそうに思える。
しかし、ここでちょっと http://foo.org/dtd/dtd.dtd
にある %mod.mod;
というパラメタ実体参照の意味を考えて欲しい。mod.mod
の実体は SYSTEM "http://foo.org/mod/mod.mod"
と宣言されているので、%mod.mod;
は http://foo.org/mod/mod.mod
の内容を参照するという意味になる。しかし、ここで言う参照とは、文字通り「(現在のファイルを離れて)実体のファイルを参照しに行く」ということを意味するものではなく、「現在のファイルに実体を埋め込む」、もっと端的に言えば「参照を実体で置換する」という意味である。
例えば、...<!ENTITY % foo "entity" >%foo;...
という記述は ...entity...
という記述と等価である。同様に、http://foo.org/dtd/dtd.dtd
の記述は次の記述と等価であるはずである。
http://foo.org/dtd/dtd.dtd
<!-- %mod.mod; を展開 -->
<!ENTITY directory.mod SYSTEM "directory.mod" >
%directory.mod;
ところが、こうして %mod.mod;
を展開した状態で件の規則を適用すると、SYSTEM "directory.mod"
は http://foo.org/dtd/directory.mod
を表す(よって &directory;
の置換テキストは dtd になる)ということになってしまう。
また、さらに遡ると、そもそも文書型宣言の外部識別子というものも、その識別子で識別される DTD 実体(外部サブセット)を文書インスタンスに補うものと見なせる。つまり、文書型宣言の外部識別子を展開した場合、文書インスタンス自体も次のような記述がなされているものと見なしうる。
http://foo.org/doc/doc.xml
<?xml version="1.0" ?>
<!DOCTYPE doc [
<!ENTITY directory.mod SYSTEM "directory.mod" >
%directory.mod;
]>
<doc>&directory;</doc>
この記述の場合にも、件の規則では SYSTEM "directory.mod"
が http://foo.org/doc/directory.mod
を表す (&directory;
の置換テキストは doc になる)ということになってしまう。
結局、この規則では実体参照を部分的に展開した場合に、システム識別子の示すパス(実体)が食い違ってしまうということが起こりうるのである。ということで、システム識別子を URI で記述する場合には、常に絶対パスで記述することが推奨されるべきなのではないだろうか、と思った。
# と、ここまで考えて思い出したのが XHTML 1.1 plus MathML 2.0 plus SVG 1.1 DTD である。この DTD ではシステム識別子での相対パスの記述が避けられており、相対パスの基準となるパスをパラメタにするという手法が用いられている。
# 具体的には、始めに <!ENTITY % XHTML.sysid.base "http://www.w3.org/TR/xhtml11/DTD/" >
といった宣言を行い、システム識別子は SYSTEM "%XHTML.sysid.base;xhtml11-model-1.mod"
といった形式で記述する、というような規則になっているのだが、この手法は件の問題へ対処として考え出されたものなのではないだろうか。実際この手法ならば、基準のパスを変更したい場合にも、適宜 XHTML.sysid.base
を上書きするだけで済み、非常にスマートな規則化が行える。
# W3C Public Mailing List Archives などには、この DTD でこういう手法が用いられている理由についての言及を見付けられなかったのだが、実際のところどうなのだろう。この話題に関したリソースを御存知の方は、石川に御一報下さい (一応解決しました。パーザのバグについての項を参照して下さい)。
SGML におけるシステム識別子とは、必ずしも URI を記述するものではなく、システムが実体を識別できるものであればどのような符号を用いても構わないことになっている。例えば SYSTEM "XHTML DTD version 1.1"
などという記述を行うこともできる(予めシステムがこの記述から XHTML 1.1 の DTD を参照するように設定されている必要はある)。
従って、件の規則も、それ自体が SGML 的な問題を抱えている訳ではなく、予め SGML/XML パーザがシステム識別子を「宣言のあるファイルを基準とした相対パス」として解釈するよう設定されていれば、解釈自体は矛盾なく行える。
あくまで、やや不自然な定義ゆえに、場合によっては意図せぬ事態を招く虞がある、という程度の話である。モジュール化されている DTD に対して、そのフラット版の DTD を作成する際に、こういう問題が起こりうる。
MSXML には、そのものずばり「システム識別子の相対パスを宣言のあった場所ではなく実体の参照された場所を基底として解釈する」というバグがあります。saxon なんかにもあるみたいです。
改めて調べてみたら、RE: XHTML 1.1 DTD, Bug in AElfred (used by saxon) というメールが見付かりました。恐らく XHTML+MathML+SVG での変更も、この事情を受けてのものでしょう。