Jintrick.netagenda2002年09月アーカイブ → 2002年09月12日

agendaの過去ログリストを自動生成(XSLT)

長いです。

<ul>
<li>
 <a href="agenda.html" title="記事のURLが変ります。">
  <span class="desc">agenda</span>最新(更新版)
 </a>
</li>
<li>
 <a href="d20029f.html" title="記事のURLが変りません。">
  <span class="desc">agenda</span>最新(保存版)
 </a>
</li>
<li>
 <a href="d20028l.html">
  <span class="desc">agenda</span>2002-08(下旬)
 </a>
</li>
<li>
 <a href="d20028f.html">
  <span class="desc">agenda</span>2002-08(上旬)
 </a>
</li>
<li>
 <a href="d20027l.html">
  <span class="desc">agenda</span>2002-07(下旬)
 </a>
</li>
<li>
 <a href="d20027f.html">
  <span class="desc">agenda</span>2002-07(上旬)
 </a>
</li>
<li>
 <a href="d20026l.html">
  <span class="desc">agenda</span>2002-06(下旬)
 </a>
</li>
<li>
 <a href="d20026f.html">
  <span class="desc">agenda</span>2002-06(上旬)
 </a>
</li>
<li>
 <a href="d20025l.html">
  <span class="desc">agenda</span>2002-05(下旬)
 </a>
</li>
<li>
 <a href="d20025f.html">
  <span class="desc">agenda</span>2002-05(上旬)
 </a>
</li>
<li>
 <a href="d20024l.html">
  <span class="desc">agenda</span>2002-04(下旬)
 </a>
</li>
<li>
 <a href="d20024f.html">
  <span class="desc">agenda</span>2002-04(上旬)
 </a>
</li>
<li>
 <a href="d20023l.html">
  <span class="desc">agenda</span>2002-03(下旬)
 </a>
</li>
<li>
 <a href="d20023f.html">
  <span class="desc">agenda</span>2002-03(上旬)
 </a>
</li>
<li>
 <a href="d20022l.html">
  <span class="desc">agenda</span>2002-02(下旬)
 </a>
</li>
<li>
 <a href="d20022f.html">
  <span class="desc">agenda</span>2002-02(上旬)
 </a>
</li>
<li>
 <a href="d20021l.html">
  <span class="desc">agenda</span>2002-01(下旬)
 </a>
</li>
<li>
 <a href="d20021f.html">
  <span class="desc">agenda</span>2002-01(上旬)
 </a>
</li>
</ul>

このリストは、最新版の年、月、上旬/下旬 が分かっていれば、自動で生成できるはず。例えば:

<blog-archive latest="2002-09-f" />

このような空要素を一つ書いて、後はXSLTに任せることができる。もちろんDOMでやった方が速いのだけれどもー。リハビリ。

マッチさせるテンプレートと変数の設定

まず、blog-archive要素にマッチさせるテンプレートと、年、月、上旬/下旬 にバインドさせるローカル変数を作る。テンプレートを作る際にはマッチする優先順位に注意。


<xsl:template match="blog-archive">
 <xsl:variable name="year" select="substring(@latest, 1, 4)" />
 <xsl:variable name="month">
  <xsl:choose>
  <xsl:when test="substring(@latest, 6, 1) = '0'">
   <xsl:value-of select="substring(@latest, 7, 1)" />
  </xsl:when>
  <xsl:otherwise>
   <xsl:value-of select="substring(@latest, 6, 2)" />
  </xsl:otherwise>
  </xsl:choose>
 </xsl:variable>
 <xsl:variable name="division" select="substring(@latest, 9, 1)" />
</xsl:template>

リテラル結果要素を書く

次に、テンプレート内にリテラル結果要素を書く。繰り返し生成する部分は、別の名前付きテンプレートを呼び出す形にする。名前付きテンプレートは後述。

<ul>
<li>
 <a href="agenda.html" title="記事のURLが変ります。">
  <span class="desc">agenda</span>最新(更新版)
 </a>
</li>
<li>
 <a href="d{$year}{$month}{$division}.html" title="記事のURLが変りません。">
  <span class="desc">agenda</span>最新(保存版)
 </a>
</li>
<xsl:call-template name="createBlogList">
 <xsl:with-param name="year" select="$year" />
 <xsl:with-param name="month" select="$month" />
 <xsl:with-param name="division" select="$division" />
</xsl:call-template>
</ul>

createBlogListという名前のテンプレートを呼んでいる。年、月、上旬or下旬をパラメータとして渡す。

リストを繰り返し生成する為のテンプレートを書く

for-eachなどは使わない。for-eachを「繰り返し」と考えている限りは、XSLTは異常に難しい(と思う)。for-eachが繰り返しならば、apply-templatesも繰り返し。リテラル結果要素を中に書くか、外に出すかの違いが主。他にも色々あるけれども、「繰り返し」とは全然関係ない。

<xsl:template name="createBlogList">
 <!-- paramの初期値。呼び出し元から渡されない場合の値を書く -->
 <xsl:param name="year">yyyy</xsl:param> <!-- 適当 -->
 <xsl:param name="month">m</xsl:param> <!-- 適当 -->
 <xsl:param name="division">d</xsl:param> <!-- f,l 以外 -->
 
 <!-- ひと月減らす。 -->
 <xsl:variable name="premonth" select="$month - 1" />

 <xsl:if test="$division = 'l'">
  <li>
   <a href="d{$year}{$month}f.html">
    <span class="desc">agenda</span>
    <xsl:value-of select="$year" />
    <xsl:text>-</xsl:text>
    <xsl:if test="$month - 10 &lt; 0">0</xsl:if>
    <xsl:value-of select="$month" />(上旬)
   </a>
  </li>
 </xsl:if>

 <xsl:if test="$month &gt; 1"><!-- 0月は無い為 -->
  <li>
   <a href="d{$year}{$premonth}l.html">
    <span class="desc">agenda</span>
    <xsl:value-of select="$year" />
    <xsl:text>-</xsl:text>
    <xsl:if test="$premonth - 10 &lt; 0">0</xsl:if>
    <xsl:value-of select="$premonth" />(下旬)
   </a>
  </li>
  <li>
   <a href="d{$year}{$premonth}f.html">
    <span class="desc">agenda</span>
    <xsl:value-of select="$year" />
    <xsl:text>-</xsl:text>
    <xsl:if test="$premonth - 10 &lt; 0">0</xsl:if>
    <xsl:value-of select="$premonth" />(上旬)
   </a>
  </li>
 </xsl:if>

 <!-- ひと月減らして再帰呼び出し。1月の場合は呼び出さない。 -->
 <xsl:if test="$premonth &gt; 1">
  <xsl:call-template name="createBlogList">
   <xsl:with-param name="year" select="$year" />
   <xsl:with-param name="month" select="$premonth" />
  </xsl:call-template>
 </xsl:if>

</xsl:template>

というわけでXSLTリハビリ終了。

今後

憧れのdive into mark (英語)に負けないようなWeblog管理を目指したい。アメリカではBlog管理ツールが沢山あるようだけれど、ローカルで処理してスタティックな文書をputするしかないJCOMユーザーな私は、ツールに頼らず自分でやるしかない。

具体的にはカテゴリ別に分けたWeblogのアンカーリストと、各記事の双方向リンクを実現したい。XSLTだけでは無理っぽいので、全Weblog文書のDOMツリーを弄ってシンプルな(エディタで直接編集可能な)XMLを生成した後、XSLTで構造化された公開用のXHTMLに変換するとか。

カテゴリをどうやって識別するかが大問題。NMTOKENS型の属性で各記事とカテゴリを関連付けるか、属性ではなく複数の要素でやるか。どちらにしろ手作業が辛い。

その他愚痴

雑誌を立ち読みして驚いた。

雑誌の入門記事などには必ずと言っていい程 <xsl:value-of select="." /> が出てくる。何故省略しなければならないのか。カレントノードが要素ノードで、子がテキストノードしか無いならば、child::text()の方が処理が速い。不許可。self::node()のように「軸 + ノードテスト」の形で一貫させた方が遥かに理解は容易になるはずなのに、何も考えずに省略形を濫用するのも不許可。value-of要素のselect属性にノードが指定された時の、String関数の挙動についての説明もなし。動けばいいんですかそうですか。

よく分からない内は省略しない方が良いに決まっているのだけれど、読者を馬鹿にした著者はそう思わないらしい。XSLTを「簡単そうで難しいもの」にしているのはあなた方だ。

愚痴は続く

さらに悪質だと思っているのがこれ。<xsl:apply-templates />。だから何故入門の癖にselect属性を省略するのか。これでは、カレントノードリストがどう変更されたのか分からない。カレントノードリストを把握できずにXSLTなんか書ける訳が無いだろうに。少なくとも何の応用も効かない筈。select="child::node()" は知っていても書くべきだ。

<xsl:value-of select="." />と<xsl:apply-templates />を使った省略だらけの例を示して、簡単な「XSLTの概要」とか称しているものもあるが、その「例」で「実験」してみたところで、それで得られた経験は何一つ応用の効かないものだ。


webmaster@jintrick.net
公開: 2002年09月12日
カテゴリ: XSLT