前回conkeyrefの例を紹介しましたが、すこし補足します.conkeyrefは間接的なトピック、マップ、その他のトピック中の要素へのコンテンツ参照です.前回はトピック中の要素から他のトピック中の要素を参照していました.ですのでキー定義は必ずトピックを指していなければなりません.これはどういうことかといいますと、例えばctr.xmlが複合トピックであるとします.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd">
<dita>
<task id="ctr">
<title>共通タスクリソース</title>
<shortdesc/>
<taskbody>
<steps>
<step>
<cmd id="reset_to_default">工場出荷時の設定に戻すには、リセットキーを押します.</cmd>
</step>
</steps>
</taskbody>
</task>
<task id="ctr_2">
<title>共通タスクリソース2</title>
<shortdesc/>
<taskbody>
<steps>
<step>
<cmd id="reset_to_default">工場出荷時の設定に戻すには、パネルのリセットボタンを押します.</cmd>
</step>
</steps>
</taskbody>
</task>
</dita>
<!DOCTYPE dita PUBLIC "-//OASIS//DTD DITA Composite//EN" "ditabase.dtd">
<dita>
<task id="ctr">
<title>共通タスクリソース</title>
<shortdesc/>
<taskbody>
<steps>
<step>
<cmd id="reset_to_default">工場出荷時の設定に戻すには、リセットキーを押します.</cmd>
</step>
</steps>
</taskbody>
</task>
<task id="ctr_2">
<title>共通タスクリソース2</title>
<shortdesc/>
<taskbody>
<steps>
<step>
<cmd id="reset_to_default">工場出荷時の設定に戻すには、パネルのリセットボタンを押します.</cmd>
</step>
</steps>
</taskbody>
</task>
</dita>
そしてキーで「共通タスクリソース2」の方を参照したければ、キー定義に明示的に2番目のトピックを識別できるようにしてやらねばなりません.つまり
<keydef keys="COMMON_TASK" href="ctr.xml#ctr_2"/>
のようにキー定義する必要があります.
さて今回は鬼門のkeyrefです.恥ずかしながら私がkeyrefでつまづいてしまった原因は、実はDITAの仕様を読まずに先にOASISにあった周辺文書を読んでしまったからです.
DITA 1.2 Keyref: Feature Description
https://www.oasis-open.org/committees/download.php/35292/DITA%201.2%20KeyRef%20Feature%20Description_Final.pdf
https://www.oasis-open.org/committees/download.php/35292/DITA%201.2%20KeyRef%20Feature%20Description_Final.pdf
この15ページのPDFを印刷して出張のバスの中で何回も読んだのですが正直まったくと言ってよいほどわかりませんでした.この前遅ればせながらDITA1.2の仕様を読み直して、やっとそのなんたるかがわかりました.これはまさにkeyref属性のところに書いてあるのですが、
"The keyref attribute provides an indirect, late-bound reference to topics, to collections of topics (ditabase), to maps, to referenceable portions of maps, to non-DITA documents, to external URIs, or to XML content contained within a key definition topic reference. "
がkeyref属性の定義です.拙訳ですと「keyref属性は間接的で、実行時に解決されるトピック(他は省略)、非DITA文書、外部URI、キー定義のtopicrefにあるXMLコンテンツへの参照を提供する」となります.つまりコンテンツ参照(コンテンツの参照/取得)のconkeyrefに比べると欲張りな仕様なのです.
・keyrefにより、トピックを間接参照できます.⇒参照の解決だけです.
・keyrefにより、外部URIを間接参照できます.⇒参照の解決だけです.
・keyrefにより、topicrefにあるXMLコンテンツを間接参照できます.⇒この場合XMLコンテンツを参照元に持ってきます.
(これだけではありません)
・keyrefにより、外部URIを間接参照できます.⇒参照の解決だけです.
・keyrefにより、topicrefにあるXMLコンテンツを間接参照できます.⇒この場合XMLコンテンツを参照元に持ってきます.
(これだけではありません)
順に見てゆきたいと思います.トピックの間接参照はどのように使うのでしょうか?
まずキーの定義自体は簡単です.conkeyrefの場合と変わりません.
<keydef keys="PRODUCT_SPEC" href="~.xml"/>
こんな例を考えてみました.バイクのユーザーズマニュアルです.構成はものすごく単純で、「エンジンをかける」「走行する」「止まる」「エンジンを切る」「製品諸元」から成ります.「エンジンをかける」~「エンジンを切る」は製品ですべて同じ内容で、「製品諸元」がバイクにより異なるとします.そうすると、製品諸元のトピック(リファレンス)をkeyrefで参照し、バイク毎にkeydefでキーを定義し、実行時に.ditavalファイルを与えることによりDITA-OTにproduct属性で取捨選択させて出力を切り替えます.図で示すと次のようなものになると思います.
すこし単純化しすぎた例ではありますが、マップもトピックも変えずに.ditavalファイルを選択するだけで出力物を切り替えることができます.これは考えてみるにスゴイことです.
さてこの例の場合、マニュアルで製品毎に異なっているものは「製品諸元」というトピックという前提でした.実際には事情はそんなに単純なことはなく、「エンジンをかける」という操作自体は同じでも、エンジンキーの位置や形状が異なり、載せる画像が製品毎に違うでしょう.それでも、その画像もkeyrefして製品毎に切り替えてやることは可能です.
<!--マップのキー定義-->
<keydef keys="ENGINE_KEY_ON" href="sr125_engine_key_on.jpg" format="jpg" product="sr125"/>
<keydef keys="ENGINE_KEY_ON" href="tys125_engine_key_on.jpg" format="jpg" product="tys125"/>
<keydef keys="ENGINE_KEY_ON" href="sr125_engine_key_on.jpg" format="jpg" product="sr125"/>
<keydef keys="ENGINE_KEY_ON" href="tys125_engine_key_on.jpg" format="jpg" product="tys125"/>
このように考えると、製品系列を捨象した「抽象的なバイクのユーザーズマニュアル」というものを作り、keydefを切り替えて各製品毎にpubilishするということも夢ではないように思えます.フィルタリングは.ditavalファイルでやっていますが、CMSはこういうクェリー機能を提供しているので、.ditavalファイルを使わなくとも十分可能でしょう.
この例はマップからトピックを参照するkeyrefを使用しました.あとトピック内のimageから外部URIを参照するkeyrefの例を示しました.トピック内からトピックを参照するのにkeyrefを使用して動的に参照先を切り替えるというのは、どのような実践的なuse caseがあるのかちょっとわかりません.
この次は、keyrefによる「キー定義のtopicrefにあるXMLコンテンツへの参照」について紹介したいと思います.
※ 今回私の大好きなフランスのScorpa社のオートバイの写真をサンプルにつかわさせていただきました.あらかじめおことわりし、お礼を申し上げます.