SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

ホームページ製作を楽にする7つのXSLTスクリプト

XSLTでXHTMLを操作しホームページ作成作業を半自動化する

5.目次の自動生成

 指定されたディレクトリに存在するファイルのタイトルを読み出し、目次を自動的に生成します。タイトルを読み出されるファイルは、XML文章である必要があります。

変換元ファイル
<?xml-stylesheet type="text/xsl" href="sample.xsl"?>

<html>
<head>
  <title>5.目次自動生成</title>
</head>
<body>
<h1>5.目次自動生成</h1>

<p>
  対象ファイルに&lt;title&gt;が存在すれば、その内容が目次に表示されます。
</p>

<p style="background-color:#eeeeee; padding:5px;">
<cdoe>
<![CDATA[
  <index path="..." sub="..." filename="..."/>
]]>
</cdoe>
</p>

<dl>
  <dt>path</dt><dl>目次に載せるファイルがあるフォルダの
(変換元ファイルから見た)相対パス</dl>
  <dt>sub</dt><dl>サブフォルダも検索するか否か。yesかnoで指定</dl>
  <dt>filename</dt><dl>目次に載せるファイルのファイル名</dl>
</dl>

<p>
  <index path="../" sub="yes" filename="*.xml"/>
</p>

</body>
</html>
変換結果
変換結果

 <index>を一つ書くだけで、目次が自動的に生成されます。

indexタグの属性
属性名 説明
path 目次に載せるファイルがあるフォルダの(変換元ファイルから見た)相対パス。
sub サブフォルダも検索するか否か。yesかnoで指定。
filename 目次に載せるファイルのファイル名。
XSLTスクリプト
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                version="1.0">

  <!-- リンク先切り替え用 -->
  <xsl:import href="../3/app2.xsl"/>
  <!-- パス関係用 -->
  <xsl:import href="../4/app1.xsl"/>

  <!-- 出力モード -->
  <xsl:output method="html" encoding="Shift_JIS"/>

  <!-- 処理命令は無視 -->
  <xsl:template match="processing-instruction()"/>
  <xsl:template match="processing-instruction('xml-stylesheet')">
    <xsl:apply-imports/>
  </xsl:template>

  <!-- ノードや属性はそのまま出力 -->
  <xsl:template match="@*|node()" priority="-1.0">
  <xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
  </xsl:template>

  <!-- 目次自動生成 -->
  <msxsl:script language="JScript" implements-prefix="xmlns">
    // ファイルのリストを文字列で返す
    function initIndex(nodeList)
    {
      var node = nodeList.item(0);

      // 検索するファイルネーム
      var fileName = node.getAttribute("filename");
      if(!fileName) fileName = "*.*";

      // ファイルネームにディレクトリ名がくっついているなら外す
      fileName = fileName.replace(/^.*\\/, "");

      // サブフォルダを参照するか否か
      var sub = node.getAttribute("sub")=="yes";

      // 検索するフォルダ
      var path = node.getAttribute("path");
      path = path.replace(/(?:\/|\\)$/, "");
      if(!path) path="";

      // ファイルを検索してリストを作成する。
      var list = findFile(path, fileName, sub, "");

      // 「,」で繋いで返す。
      return list.join(",");
    }

    // ファイルを検索して配列で返す
    function findFile(path, fileName, sub)
    {
      // ファイル操作オブジェクト
      var fso = new ActiveXObject("Scripting.FileSystemObject");

      // ファイル名リスト
      var list = new Array();

      // 検索対象のフォルダ
      var folder = fso.GetFolder(getLocalPath(path));

      // フォルダのファイルを列挙する
      for(var e=new Enumerator(folder.Files); !e.atEnd(); e.moveNext())
      {
        var file = e.item();
        if(checkWildcard(fileName, file.Name))
        {
          // 指定されたファイルネームである。
          // リストに追加
          list.push(path+"/"+file.Name);
        }
      }

      // サブフォルダの検索をするか?
      if(sub)
      {
        // サブフォルダを検索する。
        for(var e=new Enumerator(folder.SubFolders);
            !e.atEnd(); e.moveNext())
        {
          var folder = e.item();
          // サブフォルダ内のファイルのリストを作成し、
          // 現在のリストと結合する。
          var list2 = findFile(path+"/"+folder.Name, fileName, sub);
          list = list.concat(list2);
        }
      }

      // リストを返す
      return list;
    }

    // 指定したワイルドカードがファイル名にマッチするかどうか返す
    function checkWildcard(wildCard, fileName)
    {
      // ワイルドカードを正規表現に変換する
      // メタ文字のエスケープ
      wildCard = wildCard.replace(/[$+{}|.()\[\]\^\/\\]/g, "\\$&amp;");
      // *と?が使える
      wildCard = wildCard.replace(/\*/g, ".*");
      wildCard = wildCard.replace(/\?/g, ".");
      // 全体にマッチしなければならない。
      wildCard = "^"+wildCard+"$";

      // 正規表現生成
      var regexp = RegExp(wildCard);

      // ファイルネームが正規表現にマッチするかどうか返す
      return regexp.test(fileName);
    }
  </msxsl:script>

  <xsl:template match="index">
    <ul>
      <xsl:call-template name="printItem">
        <xsl:with-param name="list" select="xmlns:initIndex(.)"/>
      </xsl:call-template>
    </ul>
  </xsl:template>

  <!-- リストを分解しながらリストとして出力 -->
  <xsl:template name="printItem">
    <xsl:param name="list" select="''"/>
    <xsl:choose>
      <xsl:when test="contains($list, ',')">
        <xsl:call-template name="printItem">
          <xsl:with-param name="list"
               select="substring-before($list, ',')"/>
        </xsl:call-template>
        <xsl:call-template name="printItem">
          <xsl:with-param name="list"
               select="substring-after($list, ',')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <li>
          <a>
            <xsl:call-template name="switchHref">
              <xsl:with-param name="href" select="$list"/>
            </xsl:call-template>
            <xsl:choose>
              <xsl:when 
 test="string(document(xmlns:getLocalPath($list))//title)!=''">
                <xsl:value-of 
 select="string(document(xmlns:getLocalPath($list))//title)"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="$list"/>
              </xsl:otherwise>
            </xsl:choose>
          </a>
        </li>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

 <index>に対するテンプレート<xsl:template match="index">printItemテンプレートを呼び出します。その際、initIndex()の戻り値をパラメータとして渡しています。initIndex()は指定された条件でファイルを検索し、ファイル名をカンマで区切って返します。

ファイル名1,ファイル名2,...,ファイル名n

 パラメータ付きで呼び出されたprintItemテンプレートは、パラメータで渡された文字列にカンマが含まれているならばそこで分割し、それぞれの文字列をもう一度自分自身に渡します。つまり、再帰処理を行っています。

 分割を繰り返していくうちに、printItemテンプレートには単一のファイル名が渡されるようになります。その時、printItemテンプレートはそのファイル名を元にリストの項目を出力します。

 今回のスクリプトでは、「3.ローカルテスト時とアップロード時でのリンク先の切り替え」の「app2.xsl」や、「4.ヘッダとフッタの自動追加」の「app1.xsl」を読み込んで使用しています。

次のページ
6.キーワードの自動リンク

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

ひだちのいろ(ヒダチノイロ)

大学生。IT関係の仕事につくのが夢です。かやねずみの巣

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1055 2008/08/26 13:37

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー