XT500来たれども...

バカなのでドイツからXT500を個人輸入しました.1年かかってやっと自宅に到着、通関通って予備検査もなんとか通ってます.というか、XT500は日本では形式型番1E6なんですが、ヨーロッパ向けの車体だったので1U6です.この違いは大きく、日本のYAMAHAは主要諸元のデータを一切出してくれず、予備検請け負ってくれたバイク屋さんが四苦八苦してドイツから取り寄せてくれました.おかげで陸運局はやっと書類を受け取ってくれました.
で車体はバイク便で届いたのでさっそくエンジンかけてみたんですが、あえなく撃沈.フロントスプロケオイルシールがぶっ飛んで、完全なオイル漏れです.ガックシ心が折れました.地元のバイク屋さんに診てもらいます.あとこんな状態ではありますが、自分で車検証を取りに松本まで行かねばなりません.先は長いです.

XT500
オイルシールぶっ飛んでオイル漏れ

重複したtopic/@idを拾い出す

DITAでオーサリングされる方ならだれでもtopic/@idはDTDでいうところのID属性だから、そのトピックの中でユニークじゃなければならないと教わります.ではmapやbookmapで多くのトピックを束ねる場合はどうなのでしょうか?マップに対応する文書全体でtopic/@idはユニークでなければならないのでしょうか??
実はDITAの仕様、DITA-OTの実装は文書全体でtopic/@idがユニークであることを強制していません.同じtopic/@idがあっても別ファイルならば関係ありません.同じファイル名でもファイルシステム上で階層が別になっていれば問題になりません.ずいぶんと緩やかなのです.
ただしCMSは違います.CMSにもいろいろありますが、トピックを一意のオブジェクトIDで識別するような(つまりCMSの中で物理的なファイルシステムのような階層を持たない)アーキテクチャCMSの場合、システム全体でtopic/@idはユニークであることが要求されます.もちろんCMSのシステムがそれを保証しています.
さて今回はたまたま文書全体でtopic/@idをユニークにしなければならないという要件が発生してしまいました.フツーはそんなこと要らないので、ごく特殊な場合です.
topic/@idをユニークにしなければならないということは、実は「もしもユニークでない場合」、そのユニークでないtopic/@idを利用者にわかるように出してやらねばなりません.このユニークでないというのは、いつもトピックをOxygenのようなXMLエディタで、テンプレートからこしらえればなんの問題もなくOxygenが自動的にユニークなtopic/@idを振ってくれます.でもですね、いったん作ったトピックを、エディタの「ファイル」- 名前をつけて保存」としてしまうと一発でtopic/@idの重複は発生してしまいます.
これって意外と発生しやすいので、XSLTスタイルシートで処理するときのチェックは非常に大事になります.
ユニークでない、つまり重複しているものを拾い出すって、意外とやったことがないものです.
でもググれば世界には同じことを考えている方はかならずいらっしゃいます.今回見つけたのはXSLTの鬼才Dimitre Novatchevの方法です.
stackoverflow.com

$vSeq[index-of($vSeq,.)[2]]

たったこれだけです. $vSeq は重複をチェックするシーケンスです.この謎解きは上記のStackoverflowの記事か以下の解説を読むと良いです.

XPath 2.0 Gems: Find all duplicate values in a sequence (Part 2)
dnovatchev.wordpress.com

私の場合、最終はPDFでしたので、すべての文書のマップとトピックは「マージ後中間ファイル」にまとまります.以下が模式的に、その中間ファイルを処理してチェックしているコードです.(全部は載せてないのでイメージだけご理解ください)

    <!-- map or bookmap -->
    <xsl:variable name="root"  as="element()" select="/*[1]"/>
    <xsl:variable name="map" as="element()" select="$root/*[contains-token(@class,'map/map')][1]"/>
    <xsl:variable name="topics" as="element()*" select="($root/* except $map)/descendant-or-self::*[contains-token(@class,'topic/topic')][@oid => exists()]"/>

    <xsl:variable name="oidSeq" as="xs:string*">
        <xsl:for-each select="$topics">
            <xsl:variable name="topic" select="."/>
            <xsl:sequence select="$topic/@oid => string()"/>
        </xsl:for-each>
    </xsl:variable>

    <xsl:template match="/">
        <xsl:call-template name="oidDupicateCheck"/>
        <xsl:copy>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <xsl:template name="oidDupicateCheck">
        <xsl:variable name="duplicates" as="xs:string*" select="$oidSeq[index-of($oidSeq,.)[2]]"/>
        <xsl:if test="$duplicates => exists()">
            <xsl:call-template name="errorExit">
                <xsl:with-param name="prmMes" select="ahf:replace($stMes1012,('%dup'),($duplicates => string-join(',')))"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

テストしてみるとバッチリ動きました.いやXSLTXPath)って奥が深いです.
※ しかし、はてなのコードハイライトは実にイマイチですねぇ.

自然の弁証法

未来に対する唯物論的確信.これはエンゲルスの「自然の弁証法 序論」の最終部分です.書かれたのは1875年とされています.日本ではまだ明治の始まり、自由民権運動が始まり、また西南戦争が起こる前になります.宇宙規模の未来への透徹したエンゲルスの考察には感動を覚えます.

或る永遠の循環過程のなかで物質は運動しているのである.この循環過程が時間・空間のなかでどんなにしばしばどんなに無慈悲に起こるにせよ、幾百万の太陽と地球が生まれまた滅びるにせよ、或る太陽系のたった一つの惑星上だけでも有機的生命の諸条件が作り出されるまでにどれほど長くかかるにせよ、諸生物の仲間から思考力のある脳髄を持った動物が進化してきて、短期間だけ生存できる諸条件を見いだし、やがてまた容赦なく絶滅させられてゆく、そのときまでに、どれほど数多くの生物がこれに先立って出現しまた滅亡しなければならないにせよ、ーーわれわれは確信する、物質はどんなに変転しても永遠に物質でありつづけ、その属性はどれ一つ失われえず、それだから物質は、地球上で自分の最高の精華である思考する精神を再び絶滅してしまうであろうその同じ鉄の必然性をもって、この思考する精神を別の場所で別の時に再び生み出すに違いない、と.

新日本出版社 自然の弁証法《抄》p.30

自然の弁証法 《抄》

https://www.amazon.co.jp/%E8%87%AA%E7%84%B6%E3%81%AE%E5%BC%81%E8%A8%BC%E6%B3%95-%E6%8A%84-%E7%A7%91%E5%AD%A6%E7%9A%84%E7%A4%BE%E4%BC%9A%E4%B8%BB%E7%BE%A9%E3%81%AE%E5%8F%A4%E5%85%B8%E9%81%B8%E6%9B%B8-%E3%82%A8%E3%83%B3%E3%82%B2%E3%83%AB%E3%82%B9/dp/4406027556/

やってはいけないDITA(その3)

やってはいけないDITAの3番名は「過剰なtopicの再利用」です.DITAを導入する時、書きたいことを細かな粒度のtopicに分割し、それをmapで束ねてパブリッシングするんですよ~!と必ず教わります.なので多くの人は素直に文書を細かなtopicに分割します.

ここまではなんの問題もないんですが、世の中の技術文書は単にtopicに分割してため込めばなんとかなるというほど甘くはないのです.当然細かに分割したtopicは様々なmapから再利用されるようになります.ここで弊害が出てきてしまうのです.

@toc="no"のtopicrefで示されたtopicは本文中でもtopic/titleを出さない

最悪なワガママがこれです.topicの内容は再利用したいが、topic/titleのは本文中に出したくないというものです.もちろんtopicref/@toc="no"ですから、PDFのブックマークには現れませんし、ましてや目次にも表れないように実装するのがDITAの掟です.ところがこの場合は、対応するtopic/titleすらも本文中に出現しないようにしたいというのです.

そんなことって本当にあるのか?っていうお気持ちがあるかもしれませんが、この話は最初に日本で、次に中国(zh-CNの方)からサポートリクエストがありましたので、世界でもみんなおんなじことを考えるものだな?と妙に感心してしまいました.

結論から言うと、もしあなたがDITAのXSLTプログラマーだったら、決してこんな要求をインプリメントしてはいけません.完全なDITAの使い方の誤りだからです.解決法はものすごく簡単に言えば

  • 共有したいtopicの部分を集めて、ライブラリtopicのようなものを構成します.
  • 異なるtopic/titleで出したい場合は、違うtitleの2つのtopicを作って、そのbodyの中からライブラリtopicの該当部分をconrefしてやればいいのです.(ホントはconkeyrefが良い)

ということです.ところがこのようなやり方は、「topicレベルの再利用」しか理解していないユーザーにとっては晴天の霹靂のようなものです.下手をするとDITAを導入して5年も6年も経つのに十年一日のごとく「topicレベルの再利用」を延々とやって来て、それ以上のお知恵がない場合も多々あるようです.こういうのはそろそろDITAの使用法を進歩させないと世界の水準について行けなくなります.

ちなみに、中国から来た問い合わせでは、聞いてきた人がプログラマーレベルの方でしたので、

  • topicの再利用ばかりでなく
  • 要素レベルの再利用をするように
  • 頭を転換するようあなたのボスと闘ってくれ!

ってアドバイスしておきました.まだDITAの導入したてのフェーズだったので、みんなで頭を悩ましてくれたことを期待しています.

DITAが面白いのはXSLTプログラマーレベルでこのようなユーザーのDITA Adoptionのレベルが丸見えになってしまうことです.ここで必死で戦わなければ元のユーザーも実際には幸せにならないんですが、部署の異動があったり、年齢により配転でノウハウが引き継がれなかったりと、残念な例も多いですね.

やってはいけないDITA(その2)

やってはいけないDITAの2つ目です.

PDFを埋め込んじゃう

以前ビックリしたことがあったのですがbookmap/frontmatterにいきなり次のようなtopicrefが登場していました.

<topicref href="hajimeni.pdf" format="pdf"/>

これは何を意図しているかというと、PDFの表紙や目次の前に、hajimeni.pdfで1ページを作って、読み手に最初の注意事項を読ませるものです.内容は「製品使用上の注意」とか簡単に言えば使用者が事故などを起さないようあらかじめ注意点をまとめて述べるような内容です.

しかしさすがにtopicrefでPDFを指しているのはそれまで初めてだったので驚きました.ただプラグインXSLTスタイルシートの実装は簡単で、このtopicrefに対してfo:page-sequenceを生成して、fo:external-graphicでこのPDFのパスを指定してやるだけです.

このようなbookmapのオーサリングは大きな問題を抱えています.PDFなので、プラグインXSLTスタイルシートは内容には一切タッチできません.なおかつ問題なのは、お客さんはせっかくCMSを導入したのにこのPDFの内容をCMSでは管理できない、つまり多言語展開もできないし、ましてや再利用もできないということです.

こんなことをするのは一にも二にも普通のトピックとは一風変わった内容を出力するこの部分のXSLTスタイルシートの開発費を投資するのがもったいないからと推測します.最近はMicrosoft Wordでいとも簡単にPDFができますから、お客さんはささっと「製品使用上の注意」の文書をWordで作って、CMSには画像として登録し、マップにtopicrefで書けばそれで済んでしまう方法を見つけてしまった訳です.

こういう例外的な方法を取りだすと、永久にコンテンツをCMSで一元管理することができなくなります.その時々では短期的にはXSLTスタイルシートに対する投資を削れても、DITAのマップとトピックとはかけ離れたところでPDFのコンテンツ(元のWord文書)を管理しなければならないので、その手間は永久に続きます.もしこのWord文書を各国語展開することになったら、CMSのコンテンツとは別のルートを通して、なおかつ各国語の翻訳版をバージョン管理して保管しておかねばなりません.

短期的な経費削減を考えて、長期的には損をする典型的な手法です.

本気でCMSでコンテンツを管理しようとする組織は、CMSのコンテンツをチェックアウトしてパブリッシュしてという当たり前の流れから外れる作業を極端に嫌います.たまたま便利に見えても、それは自分たちがやってきた努力を根底から崩してゆくものとわかっているからです.

まあお客さんはいろいろです.本気でやろうとしている方、この例のように昔の時代の郷愁にとらわれて、中途半端なところをうろうろしている方.XSLTスタイルシートを作る側からみれば、そういうお客さんの本気度が一発で見えてしまうので、あまりその気になっていないお客さんの仕事をやるのは気乗りがしません.終局的にはお客さんの利益にならないことがあきらかだからです.

やってはいけないDITA(その1)

DITA(Darwin Typing Architecture)の導入があちこちの組織・企業で行われています.もう今ではサブスクリプションばやりで、CMSも必要な機能だけ購入して必要な規模だけ使うので、昔は一番経営陣を納得させるのに苦労したCMS導入ってもはや問題にならないんだそうです.一番の問題はXSLTスタイルシートを作るのに必要なお金が高いんだとのこと.それでお給料をいただいている身からすると、何か悪いことでもしているのか?と思わず自答してしまいます.
でもXSLTスタイルシートってそう簡単には作れないです.そもそもお客様はCMS導入でトレーニングを積んで、コンテンツをコンバージョンしたりして作って、それで「ああしたい」「こうしたい」を私たちにぶつけてくるのですから、正直なところあんまりDITAに習熟していないです.
だから、とかくDITA本来というかDITAの仕様に決められたものを、「ご自分なりに解釈して」ここの出力はこうしてほしい、ああしてほしいと仕様がどんどん膨らみます.今回はそのような場合によく出てくる、ちゃっと首をかしげるパターンを紹介します.

「navtitleで出したい」

一番出てくるのが、topic/titleを表示するんじゃなくて、ある場合はマップのtopicref/topicmeta/navtitleもしくはtopicref/@navtitleを代わりに出力してくれ!というものです.最初は事情がとんとわからないかったのですが、これは例えば、ユーザーニュアルとサービスマニュアルで、たまたま同じトピックを再利用して使いたい、でもユーザーニュアルには大本のサービスマニュアルで出していたtopic/titleではなくて、マップに書いたnavtitleを出したいということのようです.

f:id:toshi_xt500:20220401101739p:plain

そういえば気が付いたんですが、このような要望は今まで結構ありました.(少なくとも4~5回は)

でもですね、たぶん今のDITA-OT(DITA Open Tool Kit)ではバグがあって、マップの側のtopic/topicmeta/navtitleをtopic/titleで上書きしちゃう場合があるんです.

topicmeta/navtitle is overridden by topic/title even if @locktitle="yes" #3708
https://github.com/dita-ot/dita-ot/issues/3708

Preserve topicref/topicmeta/navtitle when @locktitle is enabled #3746
https://github.com/dita-ot/dita-ot/pull/3746

お客さんからの要望もあって、必死にJavaのコード調べてプルリクエストをDITA-OTのプロジェクトに上げたんですが、マージの前のセルフチェックで何故かエラーが出てしまったためか採用してもらえせんでした.なので安心してnavtitleは使えません.もちろん手元でのテストは万全だったんですが、何故エラーになってしまったかは知るすべもありません.

で逆に何故こんなお話がお客様から出るのか?と考えたんですが、それは実はお客様がトピックレベルの共有(再利用)しかしていないから、そのまま何とかするために言われた要求であるということです.タイトルだけ変えたいということは翻って、topic/bodyは同じで良いことになります.

ならばライブラリ的なトピックを作って、サービスマニュアルのトピックからも、ユーザーマニュアルのトピックからもそのbodyの配下の要素をconrefなりconkeyrefなりしていただく、そして各々にふさわしいtopic/titleをつけていただく.で万事解決します.以下はdivで要素を再利用する例です.

f:id:toshi_xt500:20220401101645p:plain

これはそれまでの、Microsoft Wordなんかでパブリケーションをしていたステップから始まったとすると、まずマップとトピックで書くというのはDITAの基本なので、まずそれはいいんですが、そこから先に実は再利用はまったく進んでいないということになります.DITAは「トピックの再利用」がすべてではありません.もっともっと工夫すれば、自在にコンテンツを操れます.でもこういうことを理解するのはお客さんにとっては至難の業のようです.

なかなかDITAの再利用戦略を立てるって大変です.汗をかかなければなりません.でももしその努力を怠って、延々とトピックを書き続けていたら、それは単にそれまでやっていたMicrosoft Wordが形だけトピックに変わって、CMSにひたすら蓄積されているだけのことになります.

つまり、自らのコンテンツを支配(管理)するのではなくて、実はいまだにたまり続けるコンテンツに支配され続けていることに気が付かないだけなんですね.日本の企業ってそういうところは本当に閉鎖的で、せっかく世界規格であるDITAを使っても学ぼうとしない、外部からの指摘に耳を傾けないです.

日本でもDCJ(DITA Consortium Japan)で紹介された先駆的な企業さんは本当にこういう点で努力して汗をかいておられます. はたから見ていても尊敬するくらい喧々諤々と議論して自分たちのコンテンツを良くすることに真剣です.

そういう努力をしないと、数年前のDCJ主催のDITA Festaで紹介された有名な言葉「なんちゃってDITA」の導入になってしまうのでしょう.

バイクは冬眠中です

バイクは冬眠中です.ビラ配り専用のセローも、去年買ったSRも.バッテリーを外したんですが、右は台湾YUASAのセローで左はSRのもの.セロー(225CC)はセルがあるので排気量の大きなSR(400CC)より大きい. SRはミニバイク並みですね.SRはキックオンリーなのでEFI(Electronic Fuel Injection)を動作させるのにバッテリーは必須.上がっちゃうともはやどうにもならないと原田モータスさんから念を押されました.昔はキャブだったのでバッテリー上がってもガソリンが回ってキックして火花が飛べばなんとかなりましたが、今のバイクはイモライザ―もついていて電子部品の塊ですね.昔の方がシンプルで良かった.
バッテリーは家に置いて時々充電しています.

f:id:toshi_xt500:20220223134355j:plain
SRとセロー225のバッテリー
f:id:toshi_xt500:20220223134430j:plain
バッテリーを外したSR