おかえりなさい !
私の前回の記事を思い出していただいていると思いますが、前回はスタイルシートを使って別のスタイルシートに新しい機能をコンパイルする方法の概念を実例を使って説明しました。具体的には、簡単な実行トレース・ツールの作成方法を紹介しました。このツールは、スタイルシートを実行すると出力文書に「あるコメント」が生成されるようにスタイルシートを自動的に変更するものでした。「あるコメント」とは、出力文書のどの部分がどのテンプレートで作成されたかを示すコメントでした。
しかし前回の記事の締めくくりで挙げたように、私が作成したその基本バージョンは制限がかなり多く、改良する方法はいくつかあるように思います。前回概念を示しただけのそのツールを今回の記事で、不足している機能をいくつか追加して、ずっと実用的なツールに変えます。
注: 前回作成したコードを理解していただいているものとして、今回はその変更に焦点を合わせていきます。 第1回 をまだお読みでない方は、是非ともそれにざっと目を通してからこの先にお進みください。
私のオリジナル・ソリューションでの制限の1つは、各テンプレートが実行を開始したときと終了したときに
match
パターンだけが報告されることでした。しかし実際は、テンプレートはすべて
match
の結果として実行されるわけではありません。代わりに (あるいは追加で)
name
が使われていることがあり、これらは
<xsl:call-template>
を使ってその名前で呼び出されます (少なくともそうして呼び出される場合があります)。さらに、
match
パターンが重複するテンプレートが複数あることもあり、そのうちどれが実行されるかは、そのときスタイルシートが実行されている
mode
によって決まります。
そういうわけで、前回作成したスタイルシート
tracexsl.xsl
を、そうした詳細な情報が報告の中に入るよう変更したいと思います。コメント・ジェネレーターをもう少し気のきいたものにするには、まずはテンプレートそのものをもっと調べる必要があるようです。関係のある
match
、
name
、
mode
という属性がそれぞれあるかどうか調べて、あるようだったら表示します。なかったものはその名前は出力しないようにすれば、それが存在しないのかあるいは空文字列に設定されているのかの違いが分かります。スタイル設定するテンプレートにテンプレート開始コメント・ジェネレーターを挿入するために実行するシーケンスを、以下のようにします。
リスト1. テンプレート開始コメント・ジェネレーターの挿入
<tracexsl:text xml:space="preserve"> </tracexsl:text>
<tracexsl:comment>
<xsl:text>[TraceXSL Begin]</xsl:text>
<xsl:if test="@match">
<xsl:text> match="</xsl:text>
<xsl:value-of select="@match"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="@name">
<xsl:text> name="</xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="@mode">
<xsl:text> mode="</xsl:text>
<xsl:value-of select="@mode"/>
<xsl:text>"</xsl:text>
</xsl:if>
</tracexsl:comment>
<tracexsl:text xml:space="preserve"> </tracexsl:text>
|
(
tracexsl:
プレフィックスがダミーの名前空間にバインドされていますが、このプレフィックスは、生成されるスタイルシートにこれらの結果のリテラル要素がコピーされるときに正式のXSLT名前空間に再バインドされるということを思い出してください。私のスタイルシート用スタイルシートがそんなことをできるのは、
<xsl:namespace-alias>
ディレクティブのおかげです。もし
xsl:
プレフィックスを直接使ったら、これらの要素はコピーされずに直ちに実行されてしまいます。)
もちろんテンプレート終了コメントについても同じように変更できますし、したほうがよいでしょう。
これで、どの テンプレートが実行されたかということについてのデータは増えます。しかし、なぜ
それが実行されたのかはまだ分かりません。テンプレートがどのように呼び出されたかや、現在の
mode
が何であるかを知る手段は、残念ながらXSLTにはありません。ですから、これらの情報を表示するにはもう少し作業が必要です。
テンプレートの呼び出しのほとんどが、
mode
が変更されない
<xsl:apply-templates>
から始まるので、これを想定することにします。これ以外に考えられるのは、
mode
が変更される
<xsl:apply-templates>
か、
<xsl:call-template>
の2つです。これらの操作は
<xsl:template>
要素で使ったのと同じ手法で出力文書に追加コメントを生成して報告することができますが、今回はトレースする要素の内側にコメントを挿入しないで、トレースする要素の外側をコメントで囲みます。
<xsl:call-template>
が出現したときにコメントを追加するテンプレートを、以下のようにします。このテンプレートが適用されるのは、このテンプレートのすぐ次に呼び出されるテンプレートだけなので、コメントは呼び出しの前に1つだけ生成し、前と後に対で生成することはしません。
リスト2. <xsl:call-template> が出現したときのコメントの生成
<xsl:template match="xsl:call-template">
<tracexsl:text xml:space="preserve"> </tracexsl:text>
<tracexsl:comment>
<xsl:text>[TraceXSL] CALL NAME="</xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>"</xsl:text>
</tracexsl:comment>
<tracexsl:text xml:space="preserve"> </tracexsl:text>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
|
モード変更に対してコメントを生成するテンプレートを以下のようにします。実は、
mode
が実際に新しい値に変わるときだけ報告するようにして、さらに
<xsl:apply-templates>
呼び出しが終わったときにそれがどういう値に戻っているかも報告できるようにしたいと思っているのですが、そのためのうまい方法がまだ見つかっていません。いい方法が見つかったらお知らせください。そのときは一緒にこの記事を更新しましょう。
リスト3. モード変更に対するコメントの生成
<xsl:template match="xsl:apply-templates[@mode]">
<tracexsl:text xml:space="preserve"> </tracexsl:text>
<tracexsl:comment>
<xsl:text>[TraceXSL] APPLY MODE="</xsl:text>
<xsl:value-of select="@mode"/>
<xsl:text>"</xsl:text>
</tracexsl:comment>
<tracexsl:text xml:space="preserve"> </tracexsl:text>
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
<tracexsl:text xml:space="preserve"> </tracexsl:text>
<tracexsl:comment>
<xsl:text>[TraceXSL] END MODE="</xsl:text>
<xsl:value-of select="@mode"/>
<xsl:text>"</xsl:text>
</tracexsl:comment>
<tracexsl:text xml:space="preserve"> </tracexsl:text>
</xsl:template>
|
この新しいコードを実証するには、
mode
の変更と
<xsl:call-template>
が含まれるスタイルシートに対してこのコードを実行する必要があります。そのためのスタイルシートを作成するのはやめて、Xalanに付属している適合テスト用テストケースの中からあるものを引っ張り出すことにします。これが生成するのはHTMLやXMLではなくテキスト出力なのですが、私のトレースが正しく機能することを実証するにはこれで十分です。
-
tracexsl2.xsl-- スタイルシートをトレースするための改良版スタイルシート。 -
tracexsl-sample2.xsl-- Xalanテスト・スイートの中のmodes10.xslのコピー。 -
このサンプルに対してXalanを使って
tracexsl2.xslを実行すると、tracexsl-sample2.xsl.withTraceが生成されます。 -
tracexsl-sample2.xml-- 上記テスト・ケースの入力文書であるmodes10.xmlのコピー。 -
生成された
tracexsl-sample2.xsl.withTraceをXalanを使ってこのソース文書に対して実行すると、tracexsl-sample2.xml.traceResultが生成されます。
このトレース出力はいささかくどくなっていますね。大丈夫、あとでスリムにする方法を検討しましょう。むしろ現時点ではもっと詳しいトレース出力にする必要があります。重要だと思われる何かがまだ欠けているのです。
今のところ、どのテンプレートがどのようにして呼び出されたかという情報はたくさん生成されています。しかし、当たり前ですが、処理対象の半分はこのスタイルシートですが、残りの半分はスタイル設定される文書です。入力がどこからのものなのかが分からなければ、特別な出力セットを生成する意味があまりありません。
Xalanのメッセージでのアプローチを使ってソース文書内の行と列の番号を表示することを考えました。しかし標準XSLTではその情報を利用できるようにはなっていませんから、XSLT Extensionsという手段を使うしかありません。私は移植性を捨てたくないためにこれを敬遠しているので、この方法はとりたくありません。
そして実際には、XML文書内の位置を表す方法として、行と列が最も分かりやすいというわけでもありません。処理されるノードのXPathを表示したほうがもっと実用的でしょう。たとえば、"/purchaseOrder/customer/shippingAddress[2]" のほうが "line 152, column 17" よりも情報としてずっと意味があります。
XPathを自動的に生成することは、皆さんの想像よりやっかいかもしれません。言うまでもなく、実際のパス構文はまったく単純です。しかしXPath構文を設計したW3Cの作業部会は、名前空間のURI全体を記述するのではなくXML名前空間のプレフィックスを使うことにしたのです。残念なことに作業部会はそのプレフィックスを宣言する ための構文を規定しませんでした。そのためXPath自体は本来の目的ほどの意味は持っていません。私たちはXMLのコア作業部会に不満を伝えてこの問題の解決に取り組んでほしいと要求しました。コンテキスト非依存のXPathを書きようがないからです。
もう一つのちょっとした問題は、プレフィックスは文書内のどこでも再定義される可能性があるということです。そのためXPathを作成するときは、プレフィックスの衝突によるあいまいさを避けるために新しいプレフィックスを作成し、しかも作成するプレフィックスがすでに使われているものと一切衝突しないようにしなければなりません。最も簡単でしかし最も見栄えの悪い対処法は、生成されるXPathのプレフィックスに、ソース文書内にあるものを使わずに新しいものを常に 作成することでしょう。この方法はプログラムにとっては問題ありませんが、人間にとっては分かりにくくなります。
このような問題をかわすために、名前空間の影響を受けない近似XPathでよしとすることにしました。そもそもトレース・コメントはXPathプロセッサーそのものが読むためのものではなく、人間が読むためのものです。それにプレフィックスが再定義されることは比較的まれなので、今のところおそらくこれで十分です。実際はこの方法でほとんどの場合にノードのXPathとして問題なく使えます。私のコードではこのXPathをそのためのテンプレートで生成するようになっているので、もっとよいソリューションが使えるようになったらそれに置き換えるのは簡単です。
このように単純化したことで、XSLTの 疑似XPathを非常に簡単に生成できます。要約すると、ここでは次のことを行っています。
- ノードとそのノードのすべての祖先を順々に調べる。
- 各ノードの種類を検査する。
- スラッシュ、ノードの種類と名前、そして位置述語を出力する。
この位置番号は、ノードに同じ種類と名前の兄弟がある場合にどれを指しているかを表すのに必要です。これに使う各値は
<xsl-number>
が戻します。1はその最初のインスタンス、2は2番目のインスタンス、というようになります。
次のテンプレートが
<xsl:call-template>
で呼び出されてこのジョブを実行します。
リスト4. XSLTの 疑似XPathを生成するテンプレート
<xsl:template name="pseudo-xpath-to-current-node">
<!-- Special-case for the root node, which otherwise
wouldn't generate any path at all. A bit of a kluge,
but it's simple and efficient. -->
<xsl:if test="not(parent::node())">
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:for-each select="ancestor-or-self::node()">
<xsl:choose>
<xsl:when test="not(parent::node())">
<!-- This clause recognizes the root node, which doesn't need
to be explicitly represented in the XPath. -->
</xsl:when>
<xsl:when test="self::text()">
<xsl:text>/text()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::comment()">
<xsl:text>/comment()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<xsl:text>/processing-instruction()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::*">
<!-- This test for Elements works because the Principal
Node Type of the self:: axis happens to be Element.
-->
<xsl:text>/</xsl:text>
<xsl:value-of select="name(.)"/>
<xsl:text>[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::node()[name()='xmlns' | starts-with(name(),'xmlns:')]">
<!-- This recognizes namespace nodes, though it's a bit
ugly. XSLT 1.0 doesn't seem to have a more elegant
test. XSLT 2.0 is expected to deprecate the whole
concept of namespace nodes, so it may become a moot
point.
NS nodes are unique; a count isn't required. -->
<xsl:text>/namespace::</xsl:text>
<xsl:value-of select="local-name(.)"/>
</xsl:when>
<xsl:otherwise>
<!-- If I've reached this clause, the node must be an
attribute. Attributes are unique; a count is not
required. -->
<xsl:text>/@</xsl:text>
<xsl:value-of select="name(.)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
|
このテンプレートをコメント・ジェネレーターのコードから呼び出さなければなりません。この呼び出しはかなり簡単です。
<tracexsl:comment>
内に次のようなものを追加するだけです。
<xsl:text> source=</xsl:text> <tracexsl:call-template name="pseudo-xpath-to-current-node"/> |
あとは、
pseudo-xpath-to-current-node
テンプレートを、注釈が付けられるスタイルシートが見つけられるところに置くだけです。私はこれを独自のスタイルシート・ファイルにして、
<xsl:include>
または
<xsl:import>
を使ってそれを取り込んでいます。しかしこうすると、スタイルシートを変えたときにそれをサポートするこのスタイルシートがないと実行できないことになります。そのため、それが面倒だと思われるお客様には内蔵タイプの1ファイルをお渡ししたほうがよいと思います。
では、スタイルシートを変更するというのがもともとの計画ですから、このテンプレートを注釈が付けられるバージョンにコピーしましょう。私はスタイルシート用スタイルシートのマスター・コピーを保存してすべてのものを便宜的に1つのパイルで保持しておき、そこからいろいろなものをコピーすることにしました。この方法はXSLTでは大変便利です。
document('')
を使うとそれを実行するスタイルシートから読み込みを行えるからです。
私の改良版
tracexsl3.xsl
に
pseudo-xpath-to-current-node
テンプレートを追加し、さらにソース・スタイルシート内のトップレベル
<xsl:stylesheet>
要素を認識してそのすべての子のあとに
pseudo-xpath-to-current-node
テンプレートをコピーするテンプレートも作成しました。念のために、疑似XPathテンプレートが存在するかどうかを最初に検査するテスト機能も追加しました。今後
tracexsl
をそれ自体を使ってデバッグするときがあるかもしれないのですが、そのとき同じテンプレートのコピーが2つあるとエラーになるからです。
リスト5.
<xsl:stylesheet>
に
pseudo-xpath-to-current-node
をコピーするためのテンプレート
<xsl:template match="xsl:template[not(@tracexsl:trace='no')]>
...
</xsl:template>
<xsl:template match="@*|node()" priority="-1" tracexsl:trace="no">
...
</xsl:template>
<xsl:template name="pseudo-xpath-to-current-node" tracexsl:trace="no">
...
</xsl:template>
|
関連ファイルをまとめると、次のものがあります。
-
tracexsl3.xsl-- スタイルシートをトレースするための改良版スタイルシート。 -
tracexsl-sample3.xsl-- Xalanテスト・スイートの中のmodes10.xslのコピー。 -
このサンプルに対してXalanを使って
tracexsl3.xslを実行すると、tracexsl-sample3.xsl.withTraceが生成されます。 -
tracexsl-sample3.xml-- 上記テスト・ケースの入力文書であるmodes10.xmlのコピー。 -
生成された
tracexsl-sample3.xsl.withTraceをXalanを使ってこのソース文書に対して実行すると、tracexsl-sample3.traceResultが生成されます。
前のセクションで、
tracexsl.xsl
をそれ自体を使ってデバッグする可能性について触れました。
しかし実際には、このままではあまりうまくいかないでしょう。すべてのテンプレートの出力の内部にコメントが挿入され、それには
pseudo-xpath-to-current-node
テンプレートも含まれることになります。つまり結局、生成されるコメントの内部
にコメントが生成されることになるのです。また、デフォルトのテンプレートに対してもコメントが生成されることになります。つまり属性の前にコメントが出力されることになるのですが、これは禁止されています。このままでは、トレースを実行すると私のトレース・スタイルシートは台無しになってしまいます。
(この連載の前回の記事の終わりに、出力の内部にコメントを挿入するといつも安全な結果になるとは限らないということを言いましたが、これはその格好の例です。)
確かに、こうした特定のテンプレートを特殊ケースとして扱うことはできます。しかし、同じ懸念がある別のスタイルシートをトレースするとどうなるのでしょう。必要なのはもっと一般的な解決策です。その1つは、ユーザー
のどのテンプレートがトレースしても安全で、どのテンプレートがトレースすると安全でないかを、ユーザーが
tracexsl.xsl
に伝えることです。
しかし、スタイルシートの開発者はその情報をどうやって私に伝えることができるのでしょうか。1つの簡単な解決策は、トレース・コードを追加できるかできないかをテンプレートごとマークするよう彼らに頼むことでしょう。(現実的にはおそらくデフォルトを決めておいて、例外に対してだけマークすればよいようにすることになるでしょう。)そうすれば、そのマークがあるまたはないテンプレートだけを処理するようテンプレート用テンプレートを変更できることになります。
では一体、どんなマークにすればよいのでしょう。属性にしてはどうでしょう。XSLTの要素は、名前空間が異なる属性なら追加して持たせることができます。追加属性の名前空間が異なっていればスタイルシート・プロセッサーはその属性を無視できることが分かります。すでに
tracexsl:
プレフィックスの名前空間を定義済みなので、これを使いましょう。これを使うと、その属性は私のトレース・システムに対するコマンドだということも分かりやすくなります。
たとえば、
tracexsl:trace="no"
という属性を持つ
<xsl-template>
要素にはトレース・コメント・ジェネレーターを追加できないようにしてはどうでしょう。これを実装するには、私のテンプレート用テンプレートの
match
条件を変更して、処理に取りかかる前にこの属性を検査してから心配なテンプレートにそのフラグを追加するようにします。
リスト6.
match
条件の変更とトレース対象外テンプレートのマーキング
<xsl:template match="xsl:template[not(@tracexsl:trace='no')]>
...
</xsl:template>
<xsl:template match="@*|node()" priority="-1" tracexsl:trace="no">
...
</xsl:template>
<xsl:template name="pseudo-xpath-to-current-node" tracexsl:trace="no">
...
</xsl:template>
|
これでうまくいくでしょう。でも、もう少しやりたいことがあります。
すでに触れたように、プロセッサーがテンプレートの処理に入るたびにそして終了するたびにコメントを作成すると膨大な量の情報が作成される場合があります。しかしそのほとんどはデバッグしようとしている問題にはおそらく関係のないものです。
tracexsl:trace="no"
を使って不要なものを削ることはできますが、そうするとトレースの対象を変更しようと思うとそのたびにスタイルシートを変更して、その属性を追加したり除去したりしなければならなくなります。
それよりも、テンプレートを適当なグループにまとめて、
tracexsl.xsl
を実行するときにトレース対象のグループを指定するという方法のほうがうまいアプローチのように思えます。そのためには
tracexsl:trace
の振る舞いを拡張して次のようにします。"no"
はトレースされない特別なグループにします。一方他のキーワードの場合は、その値が
$tracegroups
変数の現行値の部分文字列のとき、またはその変数が空文字列に設定されている場合にトレースするようにします。なお、この属性が実際に存在することを検査する必要があります。この属性が存在しないと、
contains()
でのテストでtrue (すべての文字列に空文字列が含まれている) が戻されることになるからです。
リスト7. トレース・グループをサポートするよう改良した
match
<xsl:template match="xsl:template[not(@tracexsl:trace='no')
and ($tracegroups='' or
@tracexsl:trace and contains($tracegroups,@tracexsl:trace))]">
|
これは完全なテストではありません。たとえば、この変数にコンマで区切られたグループのリスト
"fred,ginger" が含まれていると、
tracexsl:trace
の値が
"red"
のときに一致してしまいます。しかし私の目的からすればこれで十分です。グループ名はそれぞれに固有の部分集合ではないものを自由に選べるからです。(それでも気になる方はご自由にこのスタイルシートを改良してください。)
あとは
$tracegroups
を設定するだけです。グループを変更する際にユーザーが
tracexsl.xsl
を変更しなくて済むように、
stylesheet parameters
を利用します。これはスタイル設定プロセスを開始する前にXSLTプロセッサーで設定できる変数です。このパラメーターを受け入れるには、
<xsl:stylesheet>
に次のディレクティブを追加する必要があります。
リスト8. スタイルシート・パラメーターでのトレース・グループ・リストの受け入れ
<xsl:param name="tracegroups"
select="''"/>
|
これは
tracegroups
というパラメーターを受け入れることと、そのデフォルト値を空文字列にすることを示しています。(
select
の値はXPath式を評価することで得られるので、二重の引用符が必要です。内側の '' が空文字列の式、その外側の
"" はその式を引用することを示しています。)
このパラメーターは実際にはどのように設定するのでしょう。スタイルシートへのパラメーターの渡し方の細かいところは、使うXSLTプロセッサーとその呼び出し方によってさまざまです。Xalanのコマンドライン・ツールを使う場合は、次のようにしてオプションを追加します。
Xalan-J
Process またはXalan-C
testXSLT ドライバーの場合:
-PARAM tracegroups "fred,ginger" |
Xalan-C Xalan ドライバーの場合:
-p tracegroups "fred,ginger" |
なおこれらは、 "fred"
および "ginger"
グループに属しているとマークされているテンプレートだけにトレースを追加することを要求する場合の例です。なお引用符が必要なのは、オペレーティング・システムによってはコンマがコマンドラインのトークン区切り文字として早まって解釈されてしまうからという、ただそれだけの理由からです。代わりに、そのように解釈されない文字でグループ名を区切る方法もあります。
XSLTプロセッサーを
TrAX (javax.xml.transform)
APIを介して呼び出す場合は、
Transformer
オブジェクトに対して
setParameter("tracegroups","fred,ginger")
呼び出しを行えば同じことになります。Xalan-JではなくXalan-Cを使う場合にこれに相当する呼び出しは、
XalanTransformer
オブジェクトに対する
setStylesheetParam("tracegroups","fred,ginger")
呼び出しです (
参考文献
を参照)。
その他のプロセッサーを使用する場合は、それぞれの資料でスタイルシート・パラメーターの受け入れ方の詳細を調べてください。
これで選択式トレース・スタイルシートの完成です。関連ファイルとして以下のものがあります。例として
tracexsl:trace
属性が追加されています。このスタイルシートをそれ自体に対して実行してみてください。
-
tracexsl4.xsl--tracexsl:traceとtracegroupsのサポートが追加されたtrACEXSLスタイルシート。このバージョンには動作に関するコメントが付けられています。実際に再利用するにはこのバージョンを保存することをお勧めします。 -
トレース対象のサンプル・スタイルシートとして、
tracexsl4.xslの同一コピーを使います。 -
tracexsl-sample4.xsl.withTrace-- トレース・コードが追加されたスタイルシート。なおpseudo-xpath-to-current-nodeテンプレートはtracexsl:trace="no"なので、トレース・コードは追加されていません。 -
注釈が付けられるスタイルシートの入力として、
tracexsl4.xslの同一コピーをもう一つ使います。 -
tracexsl-sample4.traceResultで (非常に冗長な) 結果が分かります。 -
tracegroupsパラメーターを"stylesheet,template"に設定してもう一度実行します。今度は<xsl:stylesheet>要素と<xsl:template>要素に一致するグループに属しているテンプレートだけがトレースされます。 -
tracexsl-sample4a.xsl.withTrace-- そのtracegroupsの設定に一致するテンプレートにだけトレース・コードが追加されたスタイルシート。なお、疑似XPathテンプレートはまだ抑制されています。 -
選択的に注釈が付けられるこのスタイルシートの入力として、
tracexsl4.xslの同一コピーをもう一度使います。このパスをtracegroupsに設定しない で実行します。 -
tracexsl-sample4a.traceResultでその結果が分かります。今度は同一のテンプレートはトレースされていないので、冗長の程度が格段に低くなっています。ご覧になってお分かりのように、このようにトレースを限定すると実際に何が行われたかが非常に 分かりやすくなります。
宿題:
パスに今度は
tracegroups
を指定したらどうなるか、想像できますか?やってみてください。
これでスタイルシートをスタイル設定する方法がお分かりになったと思いますので、これと同じ手法をほかの自動機能強化にも応用することができます。たとえば、コメントではなく
<xsl:message>
呼び出しを生成するスタイルシートを作成できます。このときはUche
Ogbui氏が以前このdeveloperWorks に書いた手法 (
参考文献
を参照) をおそらく使うことになるでしょう。
今回の簡単な
tracegroups
メカニズムを、もっと高機能なものに置き換えることもできます。興味深いものの一つとして、XPathのパスを使って、テンプレートをトレースしてよいかどうかをテストし、EXSLT
Dynamic XPath Evaluation拡張 (
dyn:evaluate()
)
を呼び出してそれを実行することが考えられます。EXSLTライブラリーはすべてのXSLTプロセッサーでサポートされているわけではありませんが、それが存在
するときの少なくともその振る舞いを予測できる程度には標準化されています。
もう少しクリーンアップも必要でしょう。
<xsl:namespace-alias>
アプローチをやや厄介なものにしているのは、別名プレフィックスの名前空間の宣言が、そのプレフィックスが使われているところで行われがちだということです。つまり、そうした宣言が文書全体にわたって散らばっているということになります。そのプレフィックスを、ファイルの先頭に生成される
<xsl:stylesheet>
ディレクティブで一度だけ宣言するようにすれば随分すっきりします。これは
XSLT 1.0
ではちょっと見栄えが良くありません。名前空間ノードを明示的に作成する唯一の方法は、それを使う一時的要素を作成してそこから引用することです。
リスト9. 名前空間ノードを作成するための一時的要素の作成
<xsl:variable name="dummy">
<xsl:element name="tracexsl:x" namespace="http://www.my.net/my markup"/>
</xsl:variable>
<xsl:copy-of select="xx:node-set($dummy)//namespace::*"/>
|
(XSLT 2.0に新しいディレクティブ
<xsl:namespace>
が導入されることが予想されます。これは的確な解決策になるでしょう。)
このプロジェクトをスタートしたときに触れたように、
<xsl:namespace-alias>
を使わずに
<xsl:element>
を使って、標準
xsl:
プレフィックスを使った新しい要素をスタイルシートに明示的に生成するようにすることもできます。この方法はトレース・ツールの作成と保守が少し難しくなるかもしれませんし、トレース・ツールが生成したディレクティブがどれなのか分からなくなるのですが、出力がすっきりすることを考えればそうしたことを犠牲にする価値はあるかもしれません。
トレース情報を分析したいユーザーもいるかもしれません。分析対象としてたとえば各テンプレートが呼び出された回数が考えられます。トレース出力をスキャンしてコメントを見つけ、それをソートしてカウントするというスタイルシートを作成することももちろん可能です。もしそれが最大の関心事だったら、アナライザーが処理しやすいようにコメントの内容を変更したほうがよいでしょう。おそらく完全に何かほかの注釈に変更することになるかもしれません。しかし移植可能なソリューションである必要がない場合は、この種の測定はXalanの
TraceListener
として作成すれば簡単になることがあります。
すでに触れたように、(疑似XPathでの近似ではなく)
完全に正確なXPathを作成しようとすると、もう少し作業が必要になります。名前空間の問題とは別に、私がこの記事で紹介したバージョンはどのソース文書からのものかを示さないことにも注意してください。このことは、
document()
関数や2次ソースのツリーが使われているスタイルシートにとっては重要になります。こうしたギャップを埋める方法があるのは分かっていますが、読者の練習課題として残しておきました。
言うまでもなく、トレースはスタイルシートのスタイル設定の一例に過ぎません。この手法をもっと広げれば、XSLT言語の拡張をまた別な方法で実装することができます。
tracexsl:trace
属性を作成したのと同じように、スタイルシートの新しい設定や完全に新規のディレクティブを作り出すことができます。スタイルシート用スタイルシートはそれを目的の効果を得るのに必要なXSLTの詳細な操作に変換します。プロセッサー固有の特性を注意深く避ければ、こうしたソリューションはどんなXSLTプロセッサーとも連動させることができます。
読者の皆さんは、きっと私が夢にも思わないアプリケーションを考え出されるでしょう。そのために必要なことは、XSLTはきれいな表示や印刷出力を作成するための単なるツールではないということを理解することだけです。XSLTは非常に汎用性のある文書プロセッサーなのです。スタイルシート自体が文書なので、スタイルシートに対して予備的処理が可能です。ですからコンパイラーは、ある意味で、まさに大変巧みなプリプロセッサーと言えます。
そしてXSLTであらゆることが可能です。XSLT自体はXMLアプリケーションなのですから。汎用性のある標準ベースのツールって、素晴らしくないですか?
<grin/>
!
-
この「第2回」で使用した
ソース・ファイル をダウンロードできます。
-
XSLTスタイルシート言語を使用するに当たっての一般的なアドバイスを参照するには、XSLユーザーのメーリング・リスト
(
http://www.mulberrytech.com/xsl/xsl-list/index.html
) が参考になります。このメーリング・リストのホーム・ページには、Dave
Pawson氏が作成している、よく尋ねられる質問 (FAQ)
のWebサイトへのリンクもあります。そのサイトには、たいへん有用な回答がたくさん収録されています。
-
著者がこの記事のサンプルを開発およびテストするのに使用した、オープン・ソースのXalan
XSLTプロセッサーについての情報は、ApacheのWebサイト (
http://xml.apache.org
)
を参照してください。Xalanの使用方法について質問がある場合は、Xalan-JおよびXalan-Cユーザーのメーリング・リストが役立ちます。それらのメーリング・リストについては、
http://xml.apache.org/mail.html
を参照してください。Xalanの開発に参加したい場合は、同じサイトに掲載されているXalan-Devメーリング・リストをお試しください。
-
XSLTの公式な定義については、W3CのWebサイトにある
XSLT Recommendation
をお調べください。
-
W3Cは、
XSLのホーム・ページ
も公開しています。このサイトには、勧告のリンクをはじめ、XPathおよびXSL-FO仕様などの関連情報、これらの標準規格をサポートするソフトウェアのリスト、XSLに関する興味深い記事やその他の情報源
(この記事で著者が言及したものの大部分を含む)
のリンク、これらのツールの提案されている将来のバージョンを記述したワーキング・ドラフト (WD)
が掲載されています。W3Cの仕様書が少々読みにくいのは確かです。というのは、1つにはエキスパートのためのエキスパートによって書かれたものだからであり、もう1つには多くのエキスパート・プログラマーはものを書くのがあまり得意ではないからです。とはいえ、ある特定のケースにXSLTが正確にどんな動きをするはずかについて公式な説明を読む必要がある場合には、このサイトから情報が得られます。
-
XSLTをコード・コンパイラーとして使用する興味深い実例については、現在開発中のDOM Test
Suiteをご覧ください (
http://www.w3.org/DOM/Test/
)。DOMは複数の言語および複数のバインディングで利用可能であるため、開発者は、テスト・スイートを抽象的なXMLベースのメタ言語で記述し、XSLTスタイルシートを使用してそれを実行可能コードに変換するという方法を採用できます。いったんメタ言語で記述したテスト・ケースは、専用のスタイルシートが用意されている任意の言語にコンパイルして実行できるため、同じテストをどこにでも適用できるようになっています。(私自身も同様のコード生成を試してみましたが、私の場合には、スタイルシートによってBML
(IBMのBean Markup Language)
を生成させ、次にBMLツールによって実行用のJavaコードに変換する作業を実行しました。)
-
そして、もちろん、
IBMのdeveloperWorks XML zone
をチェックすることもお忘れなく。広範囲に渡る記事、チュートリアル、ヒント、およびツールが掲載されています。Uche
Ogbuji氏による次の2つの記事で、この記事で説明した概念を取り上げています。「
Debug XSLT on the fly
」(2002年11月)、「
実例で学ぶEXSLT
」(2003年2月)。
-
また、
alphaWorks
からも、多くの興味深いXMLツールが見つかります。このサイトからは、IBMの最新アイデアを実現する実験的なバージョンをダウンロードできます。
Joe Kesselman氏は、20年以上IBMに勤務し、メインフレームの回路設計から、CADツール、ソフトウェア開発の調査、インターネット標準 (彼はW3CのDOM Level 2 Recommendationの著者の一人) に至るまで、様々なプロジェクトに参与してきました。最近では、apacheのxalanを含む、xsltプロセッサーに取り組んでいます。連絡先はkeshlam@attglobal.net です。