|
by Bryn Waibel and John Boylan
オンライン工作教室 : MSDN の新たな目次作成
July 10, 2000
日本語版最終更新日 2000 年 10 月 17 日
MSDN Code Center からサンプル (TOCcode.exe) をダウンロードする
MSDN ライブラリでは、250,000 個のファイルを超える MSDN 関連情報を集中的に管理しており、その目次 (TOC) は、これらデータを参照する際の最初の入り口となるものです。しかし、あまり満足なものではなく、きちんと作り直す必要がありました。
Bryn Waibel と Mike Barta が属する MSDN の Web 制作チームが、この新しい目次を作成する仕事を任せられました。幸いこの問題を解決するための方法はそれほど複雑なものではなく、XML によるデータ層を利用した目次を JavaScript で作成すれば十分でした。ここでは、我々のチームによる目次作成の様子を紹介し、さらに独自の目次を作成する際のテンプレートとなる、簡単なサンプルを提供します。
作成する目次は、プラットフォームを特定することなく機能し、管理しやすい、拡張文字セットに対応する (国際化を考慮して) ものにする必要があります。
開始時の状況
シンプルな問題であれば、逆に解決が困難になっていた可能性もあります。しかし、幸いなことに解決策は明確なものでした。これから作成する目次は、さまざまな情報源から生成される巨大なデータの集合を扱う必要がありました。また、さまざまなプラットフォームに対応し (クロスプラットフォーム)、一般的なデータ保管形式で容易に管理でき、国際化に即して拡張文字セットに対応させようともしました。さらに、ライブラリのどこからでも、目次のあらゆるノードに直接リンクできるようにすることが目標でした。
今までの目次は、Java アプレットのデータ ソースが適切にリンクされていなかったため、管理が面倒でした。データ ソースは、それぞれ種類の異なる 200 個以上のデータ セットの関連付けを記述する必要がありました。クライアントに対して同時に送信されることなく、これらのデータ セットをまとめてリンクするには、与えられたデータ セットを階層構造に手作業でコピーするしかありませんでした。
世界規模の Web アプリケーションがまだ成熟していなかった頃のアプレットは、拡張文字セットをサポートしておらず、多様化していく言語処理システムの組み合わせに対応して進歩するブラウザ技術の展開を見越して処理できるようには作られていませんでした。たとえば、Java アプレットでは、使用しているオペレーティング システムに既定の ANSI 文字セットとして Shift-JIS がある場合にのみ、Shift-JIS 文字セットを表示できました。また、Java アプレットでは、UTF-8 などの Unicode 文字セットを処理することはできなかったのです。
さらに、今までの目次のユーザー インターフェイスは、遅くて使いにくく、Macintosh コンピュータから使用することもできず、アクセスを制限する有効な方法もありませんでした。
最後の難関は独自のデータ構造でした。目次の元となる情報を再利用するには、Java アプレット独自のデータ構造を新たに実装する作業が必要でした。
現状の理解
まず、必要なものが何かを見極めなければなりません。設計の重要なポイントを確認しました。
- XML 構造 : XML データ層が必要です。問題を解決する方法はいくつも考えられますが、いずれの場合でも XML は必要です。
- データの断片化 : 接続形態にかかわらず、データをクライアントに送信するときには、データを可能な限り小さな断片 (チャンク) に分割する必要があります。Windows® 2000 チームでも同様の問題がありました。Windows 2000 のヘルプ ファイルをオンラインで提供するためのコードは、この問題を解決することから始まりました。
- ノードの同期 : 目次のそれぞれのノードは、目的の .htm ページのみが判明している場合でも、その対応するノードを検索できるくらいわかりやすくする必要があります。このデータは、更新可能でありながら、目次に加えられた変更がデータと無関係な場合には、その変更に影響されないものである必要があります。
- 情報提供者別に分割 : MSDN ライブラリのデータは、さまざまな提供者からさまざまな形式で提供されます。RTF、CHM、HTML など、これらのデータを相互に関連付け、なおかつそれぞれの独立性を維持する方法が必要です。
- 再利用性 : データを再利用可能にします。目次の章を異なる形式で表示する必要があるからといって、わざわざそのために内容を書き直すことはしたくありません。
目次の作成過程において、最終決定した設計にこれらの問題点がもたらした影響について説明します。
目次の作成
さて、目次の作成にあたって必要となる骨組みはできました。では、これら XML、JavaScript、XSL、および ASP ファイルなどのパーツを使って、組み立てを始めましょう。
目次の XML 構造
目次作成の重要なポイントは、その構造のサイズです。MSDN の目次の全体はとても巨大で、ひとつのデータ チャンクで送信することはできません。現在、250,000 個ノードが存在します。1 つのノードを 150 バイトとすると 37 MB にもなり、とてもクライアントには送信できません。
このサイズの問題を考慮し、DTD (文書型定義) を作成する際は、読みやすさを維持しつつ、できるだけ簡素にします。取り組み方としては、要素ではなく属性に重点を置きます。これにより、データを整理してまとめることができ、XML を使用して、データ型にきちんと沿った目次の構造を表現することができます。属性中心の方法では、ノード間の関係を目次内に形成することができます。一方、要素中心の方法では、目次の 1 つのノードを表す多くの XML のノードが作成されてしまいます。
我々は、HTML Help 2.0 に含まれる、最新バージョンの HXT ファイルの DTD を使用して作業を開始し、この作業で使用するスキームの作成に着手しました。作成するノードを、MTN (MSDN TOC Node) と呼ぶことにします。次に示すコードに記述されているように、それぞれのノードには 0 個以上の MTN ノードが含まれています。ここでは、MSDN ライブラリの実装には必ずしも必要ではない属性も追加しました。これらの属性は、我々のサイトにあるすべての種類の目次について説明する際に役立つものです。
ライブラリ属性
MSDN ライブラリの目次に関連する属性の一覧です。
- nodeType : この属性には、次の 3 つの値のいずれかを設定できます。
- node : 同じファイル内に子ノードを持ちます。
- leaf : 終端のノードで、子ノードを持ちません。
- collection : ほかのファイル内に子ノードを持ちます。
- prePartum : この属性は、子ノードを持つファイルを参照します。この属性の値は、ルートにおいて、この属性をポイントするノードの正確なコピーを含むファイルへのパスです。ただし、 nodeType の値が "node" で、子ノードが存在する場合を除きます。DOM (XML Document Object Model) の場合は、prePartum によってポイントされているファイルを読み込み、現在のノードを、読み込んだファイルのトップ ノードと置き換えます。
- tocPath : 目次内のノードへのゼロ インデックス パスを含む属性です。各ノードのインデックスはダッシュ (-) で区切られているため、簡単に文字列の形式から抽出して XML DOM に直接当てはめることができます。目次の最初のノードは、tocPath の値として "0" を持ちます。そのノードの 2 番目の子ノードの tocPath 値は "0-1" です。このように、目次のトップから繋がる、あらゆるノードへの直接的なパスを作成することができます。パスの領域を示すために、tocPath 値の先頭に文字トークンも追加しました。ライブラリのトップでは、このトークンは "lib" です。トークンについては、ライブラリ情報の提供者の独立性を維持する方法と共に、後に詳しく説明します。
- state : 表示依存の属性です。指定がない場合、ノードは既定の状態に従って処理されます。ノードには、次に示す 2 種類の状態があります。
- sel : ツリーで現在選択されているノードです。このノードがコンテンツをポイントしている場合は、ノードが太字で表示されます。nodeType="node" の場合は、開いている状態で表示されます。
- open : このノードが、現在のノードへのパス内にあることを示します。この属性を持つことができるのは、nodeType="node" に設定されているノードのみです。
- ref : 関連ノードの参照先であるデータへのパスを含む属性です。ライブラリでは、このパスは HTM ファイルや ASP ファイルへの URL です。
- title : 現在のノードの表示名を含む属性です。
データの断片化
我々はまず、Windows 2000 チームの、ヘルプ ファイルをオンライン化する仕事から始めました。Windows 2000 チームは、データを利用するために XML を採用し、ファイルをオフラインで HTML 形式のソリューションにビルドしていました。これは、データがユーザーに送信される前に、ほとんどのビルド ロジックを行う方法です。
Windows 2000 の方法では、Netscape と Internet Explorer の両方のバージョンが、ファイル システムに確実に保存されており、このファイルがユーザーに表示されます。しかし、この方法には明らかな欠点があります。クライアントのプラットフォームが異なる場合、複数のソリューションを用意しなければなりません。
特に Windows 2000 が最終的にリリースされる前の段階では、パフォーマンスの観点から、ヘルプ ファイルにはオフライン ビルドの方法が採用されました。Windows 2000 では、XSL 変換と XML DOM 操作が飛躍的に改善されたため、データ処理に関してもう 1 つのアプローチを採用できるようになりました。つまり我々の目次では、XML データをファイル システムに保存し、サーバー側でユーザーが求める表示形態に沿って変換します。
ノードの同期
この目次を設計する際に直面した最も微妙な問題が、ノードの同期です。Windows 2000 チームでは、連続する数値のインデックスを使ってノードを参照していました。しかし、ノードが追加されたり、置き換えられたりするたびに、同期するスキーム全体を変更するようなことはしたくないので、我々の目次には合いません。さらに、250,000 個以上もノードがあるので、同期情報を 1 か所に保存することも避けるべきです。必要なのは、ある種のハッシュ メカニズムです。まず考えられる方法は、フォルダ内のファイル名をインデックスに持つデータを、そのファイルのパスに保存します。我々は XML ファイルに情報を保存することにしました。このファイルをマップ ファイルと呼びます。
<!DOCTYPE MsdnTocMap [
<!ELEMENT MsdnTocMap (L+)>
<!ATTLIST MsdnTocMap
rootToc CDATA #IMPLIED
>
<!ELEMENT L EMPTY>
<!ATTLIST L
url ID #REQUIRED
pth CDATA #REQUIRED
>
]>
このように、マップ ファイルはとても簡潔で実用的であり、XML を使用して "名前" と "値" のペアを作成するには適切な方法です。それぞれのノードは、"名前" であるファイル名と、"値" である tocPath から形成されています。名前を ID 型の属性である url に、値を CDATA 型の属性である pth に代入します。
読み込んだファイルの XMLDOMDocument オブジェクトの nodeFromID を使用して、ファイルへのパスと、そのファイル名が得られます。これで、そのディレクトリから正しい map.xml ファイルを開き、ファイル名からそのノードへのパスを知ることができます。toc.asp ファイルで定義されている formatFileName( strFName ) 関数を使用して、ファイル名に必要な処理が施されます。toc.asp ファイルを次に示します。
function FormatFileName( sFileName )
{
if( "string" == typeof( sFileName ) )
{
// ピリオド、ハイフン、アンダースコア、コロンなど、
// 文字以外のキャラクタを削除する。(ID 属性のために必要な処理)
sFileName = sFileName.replace( /[^\w\.-_:]/gi , "" );
// 番号から始まる場合も削除する。
if ( sFileName.match( /^[\.\d]/i ) ) sFileName = "f" + sFileName;
return sFileName;
}
}
情報提供者別に分割
コンテンツのセット間の独立性を維持するために、それぞれのセットをトークンで表わしました。tocPath の説明で便利だと紹介したトークンのことです。
提供者別に名前を作成し、それぞれのトークンの最後に一意の整数を付記して、作成者のツリーの最上位ノードに設定しました。付記する整数は、簡素化するために 0 から始まる連続する整数を使いました。tocPath はマップ ファイルの中で、その提供者のコンテンツの最上位ノードから参照します。そこで、目次のトップから、それぞれの位置へ各トークンをマップする submap.xml ファイルを作成しました。このデータを保存したファイルが、もう 1 つのマップ ファイルであり、このファイルでは、 ref 属性がトークン名を値に持ち、 pth 属性がそのトークンへのパスを値に持ちます。これで、ほかの個々のコンテンツに影響を与えることなく、コンテンツを移動することができます。マスターのトークン マップ ファイルだけを更新すればよいのです。さらに、個々のコンテンツの範囲内で、コンテンツを移動することができます。そのコンテンツの外側にある、そのほかのノードの順序が壊れることはありません。1 つのデータのトップ レベルからの参照を変えることで、さまざまな種類のデータ表示を、好きなだけ作成することができるのです。
サンプル アプリケーション
ここまでは、我々の設計の中で XML がどのような役割を果たすのかを見てきました。それでは、これらすべてのパーツがどのようにしてアプリケーションを形成するかを説明します。添付したサンプル アプリケーションを見てきましょう。目次に使用しているテクニックや、目次のコードを含むサンプル アプリケーションの動作を確かめるためにノードを 1 つ作るとすれば、次の図のように表示されるのが目標です。
図 1. サンプルの目次 : Code Center の MSDN 目次のノード
"MSDN Code Examples" 以下のノードを、それぞれ独立した要素とみなします。この構造を表すには、Msdnce.xml と Ltoc0.xml の、2 つの XML ファイルが必要です。
msdnce.xml
<MsdnToc>
<MTN title="MSDN Code Examples" nodeType="node" type="none" tocPath="msdnce-0" hal="en-us">
<MTN title="The MSDN TOC" nodeType="collection" type="none" tocPath="ltoc0" hal="en-us" prePartum="ltoc0.xml"/>
</MTN>
</MsdnToc>
Msdnce.xml ファイルは、まったく単純で、すべての MSDN サンプル コードを制御するノードのみです。これには、サンプルの Ltoc0.xml を指す単一のコレクション ノードが含まれます。
Ltoc0.xml
<MTN title="The MSDN TOC" nodeType="node" type="none" tocPath="ltoc0" hal="en-us">
<MTN title="Revamping MSDN TOC" nodeType="leaf" type="file" ref="library.asp" tocPath="ltoc0-0"/>
<MTN title="ASP Files" nodeType="node" type="none" tocPath="ltoc0-1">
<MTN title="loadtree.asp" nodeType="leaf" type="file" ref="loadtree.asp.txt" tocPath="ltoc0-1-0" />
<MTN title="toc.asp" nodeType="leaf" type="file" ref="toc.asp.txt" tocPath="ltoc0-1-1" />
<MTN title="default.asp" nodeType="leaf" type="file" ref="default.asp.txt" tocPath="ltoc0-1-2" />
</MTN>
<MTN title="CSS Files" nodeType="node" type="none" tocPath="ltoc0-2" >
<MTN title="toc.css" nodeType="leaf" type="file" ref="toc.css.txt" tocPath="ltoc0-2-0" />
</MTN>
<MTN title="JS Files" nodeType="node" type="none" tocPath="ltoc0-3" >
<MTN title="toc.js" nodeType="leaf" type="file" ref="toc.js.txt" tocPath="ltoc0-3-0" />
</MTN>
<MTN title="Include Files" nodeType="node" type="none" tocPath="ltoc0-4" >
<MTN title="locals.inc" nodeType="leaf" type="file" ref="locals.inc" tocPath="ltoc0-4-0" />
</MTN>
<MTN title="XML Files" nodeType="node" type="none" tocPath="ltoc0-5" >
<MTN title="msdnce.xml" nodeType="leaf" type="file" ref="msdnce.xml" tocPath="ltoc0-5-0" />
<MTN title="ltoc0.xml" nodeType="leaf" type="file" ref="ltoc0.xml" tocPath="ltoc0-5-1" />
<MTN title="map.xml" nodeType="leaf" type="file" ref="map.xml" tocPath="ltoc0-5-2" />
<MTN title="submap.xml" nodeType="leaf" type="file" ref="submap.xml" tocPath="ltoc0-5-3" />
</MTN>
<MTN title="XSL Files" nodeType="node" type="none" tocPath="ltoc0-6" >
<MTN title="toc.xsl" nodeType="leaf" type="file" ref="tocdown.xsl" tocPath="ltoc0-6-0" />
</MTN>
<MTN title="Graphics Files" nodeType="node" type="none" tocPath="ltoc0-7" >
<MTN title="bo.gif" nodeType="leaf" type="file" ref="bo.gif" tocPath="ltoc0-7-0" />
<MTN title="bs.gif" nodeType="leaf" type="file" ref="bs.gif" tocPath="ltoc0-7-1" />
<MTN title="dc.gif" nodeType="leaf" type="file" ref="dc.gif" tocPath="ltoc0-7-2" />
</MTN>
</MTN>
Ltoc は、個別の提供者として指定された提供者を示す トークンの 1 つです。そして msdnce は、このデータの表示の名前を表す、トップ レベルの名前トークンです。両方のセットには自由に名前を付けることができます。Ltoc0.xml は、msdnce.xml よりも少し意味のあるものです。これは、このサンプルの目次を表現しています。つまり、我々の番号付け規則では、ltoc0 は ltoc コレクションの最初のノードです。
ここで、再利用性が機能する様子を直ちに見ることができます。我々はサイトのほかの目次から、ltoc0.xml を指定することができます。あたかも msdnce ファイルの 2 番目のレベルのノードであるかのように指定するだけです。また、MSDN Code Examples ノードにサンプルを好きなだけ追加することもできます。これらのノードは、相互に完全な独立性を維持しています。
map.xml
map.xml ファイルは、コンテンツを自身のサブツリー内のパスにマップします。ここで特に興味深いのは、すべてのノードが ltoc0 サブトークンを介して参照されているという点です。この方法は、同じ ltoc0.xml ファイルをポイントするために ltoc0 データを使用する、あらゆるアプリケーションに利用できます。XML を利用する側では、ltoc0 への自身のマップを準備し、map.xml ファイルを受け入れるようにする必要があります。
<?xml version="1.0"?>
<!DOCTYPE MsdnTocMap [
<!ELEMENT MsdnTocMap (L+)>
<!ATTLIST MsdnTocMap
rootToc CDATA #IMPLIED
>
<!ELEMENT L EMPTY>
<!ATTLIST L
url ID #REQUIRED
pth CDATA #REQUIRED
>
]>
<MsdnTocMap>
<L url="library.asp" pth="ltoc0-0"/>
<L url="loadtree.asp.txt" pth="ltoc0-1-0" />
<L url="toc.asp.txt" pth="ltoc0-1-1" />
<L url="default.asp.txt" pth="ltoc0-1-2" />
<L url="toc.css.txt" pth="ltoc0-2-0" />
<L url="toc.js.txt" pth="ltoc0-3-0" />
<L url="locals.inc" pth="ltoc0-4-0" />
<L url="msdnce.xml" pth="ltoc0-5-0" />
<L url="ltoc0.xml" pth="ltoc0-5-1" />
<L url="map.xml" pth="ltoc0-5-2" />
<L url="submap.xml" pth="ltoc0-5-3" />
<L url="tocdown.xsl" pth="ltoc0-6-0" />
<L url="bo.gif" pth="ltoc0-7-0" />
<L url="bs.gif" pth="ltoc0-7-1" />
<L url="dc.gif" pth="ltoc0-7-2" />
</MsdnTocMap>
submap.xml
submap.xml ファイルは、それぞれの XML 利用者が管理するマップで、データ同士の関連性を提供します。 このビューは、データの 1 セットのみ (ltoc0) を指し示すので、ノードは 1 つだけです。ltoc0 の下位領域にも同様に定義できることに注意することが重要です。そこへのパスは、submap.xml の ltoc0 に関連して簡単に参照されます。同じ .css ノードをもう 1 つのビューに含める場合は、そのノードの toccss を呼び出して、submap.xml へ ltoc0-2 として参照させます。このビューからの ltoc0-2 は、正しいパス msdnce-0-0-2 に変換されます。
<?xml version="1.0"?>
<!DOCTYPE MsdnTocMap [
<!ELEMENT MsdnTocMap (L+)>
<!ATTLIST MsdnTocMap
rootToc CDATA #IMPLIED
>
<!ELEMENT L EMPTY>
<!ATTLIST L
url ID #REQUIRED
pth CDATA #REQUIRED
>
]>
<MsdnTocMap>
<L url="ltoc0" pth="msdnce-0-0" />
</MsdnTocMap>
locals.inc
locals.inc ファイルには、表示に関するすべての情報が含まれ、アプリケーションのすべての .asp ファイルから参照されます。サンプル アプリケーションでは次のようになります。
<%
// default.asp のタイトル
var L_HomePageTitle_HTMLText= "MSDN TOC Code Example";
// テキストの方向の設定 (右から左の場合は "RTL")
var L_strBiDiMode_HTMLText = "LTR";
// メッセージ テキストの読み込み
var L_LoadingMsg_HTMLText = "Loading, click to cancel... ";
// 元となる情報
var RootDir = "msdnce.xml";
var DefaultTopic = "library.asp";
var SubMapPath = "subMap.xml";
var RootTocToken = "msdnce";
%>
ASP
目次には、2 つの .asp ファイル (loadtree.asp と toc.asp) があり、XML 処理と、クライアントに表示されるデータを用意します。
loadtree.asp
loadtree.asp ファイルは、prePartum および tocPath パラメータを持ち、tocPath で指定された場所の子ノードを出力します。出力するには、簡単な方法と難しい方法があります。希望するノードが prePartum で参照されるファイルのトップにある場合は、簡単な方法を採用します。この方法は、ファイルを DOMDocument オブジェクトに読み込むのと同じくらいシンプルです。tocDwon.xsl でファイルを変換し、結果をページに出力します。出力された結果は、ユーザー インターフェイスが処理を引き継ぎます。
難しい方法は、loadtree.asp が、prePartum で参照されているファイルにあるノードを見つけなければならないが、どこにあるのかが分からない場合に採用します。このケースは、Loadtree.asp に提供されている tocPath 属性が、読み込んだドキュメントのトップにある tocPath パラメータと異なる場合に発生します。この場合 loadtree.asp は、XML ファイルのトップ ノードの tocPath パラメータを、クエリ文字列に渡される tocPath パラメータから取り除きます。残りの断片は、移動するパスとして loadtree.asp ファイルで使用します。断片にパスが含まれなくなったら、探していたノードを見つけたことになります。そして、そのノードのコピーを作成し、変換してページに結果を反映させます。ドキュメントのルートから実行するには、スタイル シートが必要です。
toc.asp
toc.asp ファイルは、ref および tocPath パラメータを持ち、トップ レベルの目次を読み込んで、トップ レベルから tocPath で参照されているノードまで、あらゆるノードを出力します。ref で参照されているノードへのパスが見つかったら、ツリーを移動して、目的のノードにたどり着くまで不要な子ノードを切り離します。見つからない場合はトップ レベルを出力します。
XSL
XSL は、XML を XHTML に変換してブラウザに表示する際に使用されます。サーバーは、それぞれのクライアントの構成に沿って、異なる .xsl ファイルを選択することができます。XSL ファイルは、それぞれの意図が再帰的なテンプレート メカニズムを持っていると言えます。このメカニズムは、クライアントに送信される XML を HTML に変換します。次の例を参照してください。
<xsl:template match="MTN[@nodeType='node']"><!--
ノードの HTML --><xsl:apply-templates select="MTN"/></xsl:template>
<xsl:template match="MTN[@nodeType='collection']"><!--
コレクションの HTML --></xsl:template>
<xsl:template match="MTN[@nodeType='leaf']"><!--
リーフの HTML --></xsl:template>
JavaScript および CSS
JavaScript と CSS (カスケード スタイル シート) は対象のブラウザの種類によっても異なります。このサンプルで使用されているバージョンは、Internet Explorer 4.0 以降でのみ目次を表示できます。.xsl ファイルで生成される HTML は、その他のブラウザで目次のナビゲーションや目次の作成を処理する .asp ファイルのためにすべてのメッセージを処理します。
課題
以上が MSDN ライブラリの目次です。さまざまな種類のファイルに接続する必要があるときや、それらのファイルに簡単にアクセスする場合など、独自の目次を作成するときの出発点となります。今回設計した目次は、組織図、概略図などの階層構造のデータ処理にも対応した、用途の広いモデルです。
|