使っている要素を調べる

DITAは汎用なので文書モデルが大きいです.実際にマニュアル作成などの分野に使う場合、使う要素は限られているのではないでしょうか?例えばXMetaLのようなXMLエディタでオーサリングする場合、あまりに多くの(使わない要素)が候補に出るので慣れない人は大変というお話がありました.例えばpだけでもDITAの仕様では
 
( text data or dl or parml or fig or syntaxdiagram or imagemap or image or lines or lq or note or hazardstatement or object or ol or pre or codeblock or msgblock or screen or simpletable or sl or table or ul or boolean or cite or keyword or apiname or option or parmname or cmdname or msgnum or varname or wintitle or ph or b or i or sup or sub or tt or u or codeph or synph or filepath or msgph or systemoutput or userinput or menucascade or uicontrol or q or term or abbreviated-form or tm or xref or state or data or data-about or foreign or unknown or draft-comment or fn or indextermref or indexterm or required-cleanup) (any number)
 
と、こんなにあります.これが要素リストにアルファベット順で出てもまず永久に使わない要素もあります.(例えばforeignやunknownなんて決め事さえすれば絶対使わないでしょう.)こうした場合、自分用のサブセットのDTDを作ればよいのですが、実際に今どの要素をどこで使っているのか調べなければなりません.これは手でやるのは結構大変です.
 
そこで、DITA-OTを使わずに、直接ditamapを入力して使っている要素をテキストファイルに出力するスタイルシートを作ってみました.DITA-OTを使わない場合に問題になるのはカタログファイルです.ditamapやtopicにはDTDが宣言されているので、カタログファイルを使ってXSLTプロセッサにこれを解決するように教えてあげねばなりません.DITA-OTのSaxonは9.1ですが、9.4のSaxonは-catalog:というパラメータがあり直接カタログファイルを指定できます.これはとても便利で、resolver.jarさえCLASSPATHに入れておけばちゃんと動いてくれます.コマンドラインは次のようなものになります.
java net.sf.saxon.Transform -s:[ditamapのパス] -xsl:[次のスタイルシートのパス] -o:elemList.txt -catalog:D:\DITA-OT1.5.3\catalog-dita.xml
 
スタイルシートは次のように作ってみました.
 
<?xml version='1.0' encoding="UTF-8" ?>
<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 exclude-result-prefixes="xs">
<xsl:output method="text"/>   
<xsl:template match="*[contains(@class,' map/map ')]">
    <xsl:variable name="element" as="xs:string*">
        <xsl:apply-templates/>
    </xsl:variable>
    <xsl:variable name="sortedElement" as="xs:string*">
        <xsl:for-each select="distinct-values($element)">
            <xsl:sort/>
            <xsl:sequence select="."/>
        </xsl:for-each>
    </xsl:variable>
    <xsl:for-each select="$sortedElement">
        <xsl:sequence select="concat(.,'&#x0A;')"/>
    </xsl:for-each>
</xsl:template>
<xsl:template match="*[contains(@class,' map/topicref ')]" as="xs:string*">
    <xsl:variable name="map" select="ancestor::*[contains(@class,' map/map ')]" as="element()"/>
    <xsl:variable name="href" select="string(@href)" as="xs:string"/>
    <xsl:message select="'$href=',$href"/>
    <xsl:choose>
        <xsl:when test="ends-with($href,'.dita') or ends-with($href,'.xml')">
            <xsl:apply-templates select="document($href,$map)/*[contains(@class,' topic/topic ')]"/>
        </xsl:when>
        <xsl:when test="ends-with($href,'.ditamap')">
            <xsl:apply-templates select="document($href,$map)//*[contains(@class,' map/topicref ')]"/>
        </xsl:when>
    </xsl:choose>
    <xsl:apply-templates/>
</xsl:template>
<xsl:template match="*" as="xs:string*">
    <xsl:variable name="name" select="for $n in 1 to count(current()/ancestor-or-self::*) return name(current()/ancestor-or-self::*[$n])" as="xs:string*"/>
    <xsl:sequence select="string-join(reverse($name),'/')"/>
    <xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
 
出力は例えばこんなふうになります.
 bookmap/bookmeta
 bookmap/bookmeta/bookid
 bookmap/bookmeta/bookid/edition
 bookmap/bookmeta/bookrights
 bookmap/bookmeta/bookrights/bookowner
 bookmap/bookmeta/bookrights/bookowner/organization
 ...
 task/taskbody/steps/step/tutorialinfo/uicontrol
 task/title
 task/title/ph
 task/title/sup
自分の使っている要素を分析するには少しは役に立つかもです.