レベル: 初級 Benoit Marchal (bmarchal@pineapplesoft.com), Consultant, Pineapplesoft
2001年 9月 01日 今月は、骨身を惜しまないこのコラムニストが、複数のスタイル・シートに対するサポートをXMコンテンツ・マネージメント・プロジェクトに追加します。その際、TrAXURIResolver を検討し、疑似属性に関する独自のパーサーを作成します。例によって、完全なソース・コードが、developerWorksのオープン・ソース・ゾーンから提供されます。
「実用的なXML」コラムでは、Benoît Marchalが毎月、1つまたは複数のオープン・ソースXML開発プロジェクトについて進行状況を報告しています。読者は、彼の設計上の決定事項やコーディング上の選択事項に従うこともできるし、またオープン・ソース・コードについて提案を行ったり、読者自身のプロジェクトでそれを再使用したりできます。
XMの作業は継続して行われています。今月、私は複数のスタイル・シートに対するサポートを追加しました。これにより、読者からの最も一般的な提案を処理することができます。このほか、パラメーターをスタイル・シートに渡すためのオプションも追加しました。これはXMの基本公開機能を完全にするためのものです。より高度な機能に関する作業を開始するにあたり、私はディレクトリー・リーダーを組み込みました。(今回のコラムとこれまでのコラムに関連するすべてのコードをダウンロードすることができます。補足記事「コードの入手」を参照してください。)
複数のスタイル・シート
XMの最初の2つのバージョンは万能型の戦略をとっていました。この戦略は、始めはすばらしいアイデアと思われましたが、結局効果的なものではないと分かりました。もっと具体的に言えば、XMは、今までのところ、1つのスタイル・シートrules.xsl しか認識しません。このシリーズの最初の記事で述べたように、私は当初、以下のようにして、異なるXSLTテンプレートを使ってスタイルの変更を選択しようと思いました。
<xsl:template match="db:article">
<!-- rules for an article here -->
</xsl:template>
<xsl:template match="xm:Directory">
<!-- rules for a directory here -->
</xsl:template> |
しかし、私が専らXMを使って保守しているサイトananas.orgを使用してきた独自の経験から、私の当初の計画はうまく機能していないということが分かりました。また、制限を取り除いてほしいという読者の声も聞きました。結局、コンテンツ生成 (これについては後述) を開始してみると、複数のスタイル・シートに対するサポートを追加する時期にきたということがはっきりしました。
コードの入手
例によって、このコラムのコードは、CVSリポジトリーからダウンロードすることができます (参考文献を参照)。同じ場所からZIPファイルもダウンロードすることができます。今月のこのダウンロードには、サンプルの .xmlおよび .xslファイルが組み込まれています。
処理命令
読者は、私がXMに関する重要事項の1つとして、使いやすさを挙げていることを覚えておられるかもしれません。つまり私は、「どのスタイル・シートをどこに適用するか」を選択するために、構成ファイルや "ビルド・スクリプト" を使用したいとは思いません (この要件の詳しい説明については、「実用的なXML: コンテンツ・マネージメントにXSLTを使用する」を参照してください)。
スタイル・シートを選択する創意に富んだ命名規則を読者から提案してもらいましたが、解決策として最善の提案は同僚から提供されたもので、それは私にxml-stylesheet 処理命令を思い出させました。
読者がxml-stylesheet にあまり馴染みがないとすれば、それは、この処理命令が1999年7月にW3C小勧告で発表され、Internet Explorer 5.0によって普及されたからです。この処理命令は、スタイル・シート (XSLまたはCSSのいずれか) とXML文書を関連付けます。たとえば、次のようになります。
<?xml version="1.0"?>
<?xml-stylesheet href="classic.xsl" type="text/xml"?>
<?xml-stylesheet href="funky.xsl" type="text/xml" alternate="yes"?>
<article>
<articleinfo>
<title>ananas.org</title>
<!-- rest of the document goes here --> |
一般に、処理命令はアプリケーション固有のデータをエンコードします。大部分のXML文書はXML宣言で始まっており、この宣言自体が特殊な処理命令であるわけですから、読者はすでに処理命令とはおなじみです。処理命令には、ターゲット (上の例ではxml-stylesheet) が含まれており、その後にデータが続きます。ターゲットは、<? と?> 区切り文字で囲まれています。ターゲットはアプリケーションを識別しますが、アプリケーションは認識できないターゲットの処理命令を無視することになっています。
このデータの形式はまったく自由です。XMLは、そこに何が入るかを指定しません (もちろん、XML宣言の場合を除きます)。実際、歴史的に見た場合、処理命令にはPostScriptイメージまたはスクリプト、つまり、タグ以外はすべて含まれています。
宣言と同様、xml-stylesheet はW3Cによって定義されますから、この処理命令は特殊な状況を持っています。この処理命令は文書のプロローグ (つまり、最初の要素の前) の中に入っていなければならず、またそれにはいくつかの、いわゆる疑似属性 が含まれています。構文がXML属性に類似しているため、このデータは疑似属性と呼ばれます。
最も重要な疑似属性はhref であり、これにはスタイル・シートのURIが含まれています。その他の有用な疑似属性としては、type およびalternate があります。type は、スタイル・シートのMIMEタイプであり、CSSとXSLを区別する場合に役立ちます。複数のxml-stylesheet 命令がある場合、alternate は、どれが基本スタイル・シートの置き換えであるかを示します。プロセッサーは、代替スタイル・シートでユーザーにプロンプトを出す必要があります。しかしXMはバッチ・モードで働きますから、これは別の戦略を使用し、単に代替スタイル・シートを無視するだけです。
xml-stylesheet はW3C規格ですが、別途指示されない限り、TrAXプロセッサーはそれを無視します。アプリケーションは、明示的にgetAssociatedStylesheet() を呼び出し、以下のようにして処理命令を検索しなければなりません。
Source document = new StreamSource(file),
stylesheet = factory.getAssociatedStylesheet(document,null,null,null);
if(null != stylesheet)
transformer.transform(document,new StreamResult(System.out));
else
throw new XMException("Cannot find the style sheet");
|
それでもgetAssociatedStylesheet() は、XMにあってはならない2つの問題を抱えています。最初の問題は、getAssociatedStylesheet() が、頻繁に使用するスタイル・シートのキャッシュ化を困難にするということです。2番目の問題は、スタイル・シートが文書と同じディレクトリーに保管されていることを前提にしているということです。スタイル・シートを1つのディレクトリーにまとめておけば、それらの保守や共用が容易になるので、私はスタイル・シートを別のディレクトリーに入れておく方を選びます。
>
スタイル・シートの提供
スタイル・シートの選択は、まだ解決策の半ばにすぎません。私は、スタイル・シートを新規に書き直すほどではない、小さな変更を行いたいと思うことがよくあります。そのような場合の私のお気に入りの解決策は、リスト1に示されているようなパラメーターを使用することです。
リスト1: サンプル・パラメーター
<xsl:stylesheet ...>
<xsl:param name="sponsor" select="'none'"/>
<xsl:template match="articleinfo">
<xsl:if test="$sponsor='dw'">
<center>
<a href="http://www.ibm.com/developerWorks">
<img align="middle" width="136" height="24" border="0"
alt="developerWorks" src="!images/buttons/dw.gif"/>
</a>
</center>
</xsl:if>
<xsl:apply-templates/>
</xsl:template>
|
再度お聞きしますが、皆さんはどのようにしてパラメーターを渡していますか? W3Cはこれまでにメカニズムを提案してはいませんが、新しい処理命令を定義したほうが論理にかなっているように思われます。XMは、xml-stylesheet だけでなく、xm-xsl-param も認識します。xm-xsl-param の構文は他の処理命令と似ていて、以下のように、2つの疑似属性name およびvalue を使用します。
<?xml version="1.0"?>
<?xm-xsl-param name="sponsor" value="dw"?>
<article>
<articleinfo>
<title>XM</title>
|
TrAXがxm-xsl-param をサポートしていないことは明らかですが、私はすでに、XMにはgetAssociatedStylesheet() に置き換わるものが必要であるということを立証していますので、xm-xsl-param の構文解析は大した作業ではありません。
しかし、そのことは文書を2回構文解析することになりませんか? 処理命令のために1回と、XSLTプロセッサーで1回です。実際面では、構文解析を2回行っても大した手間にはなりません。それは、処理命令は文書のプロローグに入っていなければならないので、XMは文書の小さなサブセットのみを再構文解析するからです。
ProcessingInstructionHandlerおよびPseudoAttributeTokenizer
ProcessingInstructionHandler は、xml-stylesheet とxm-xsl-param を抽出するSAXContentHandler です。
このハンドラーは4つのイベントを代行受信します。setDocumentLocator() とstartDocument() は、初期化に使用されます。大量の作業はprocessingInstruction() で行われます。startElement() の場合は、プロローグの終わりをマークしますので、構文解析が停止されます。構文解析を停止するために、startElement() は例外を出します。これは間違いなくハッキングまがいのものです。例外は通常、エラー報告のために使用されますが、startElement() にエラーは含まれていません。SAXは、構文解析を停止するための明確な解決策を提供していません。
疑似属性の構文はXML属性に類似してはいますが、SAXパーサーはそれらをデコードしません。XMが独自のパーサーPseudoAttributeTokenizer を使用して疑似属性をデコードします。
PseudoAttributeTokenizer は、一度に1文字ずつバッファーをスキャンして疑似属性を探します。この命令は、どのコンパイラー解説書にも記載されている旧来のアルゴリズムを使用します。このテーマになじみがない場合は、Compiler Construction (Niklaus Wirth of Pascal fame) を読むことをお勧めします (参考文献を参照)。
コードを簡単にするために、getc() メソッドはバッファーから取り出した次の文字を戻し、putc() は、バッファーの文字を置き換えて次のgetc() 呼び出しに使用できるようにします。
PseudoAttributeTokenizer のパブリック・インターフェースは、3つのメソッドからなっています。すなわち、hasMoreTokens() はバッファーにまだ疑似属性が残っていないかどうかをテストし、nextName() は次の名前を戻し、nextValue() は次の値を戻します。
nextName() を検討してみましょう。この命令は、eatSpaces() 呼び出しの先行スペースを除去します。次にこの命令は、数字または文字を検出するまでループし、変数 (token) 内の文字を累積します。名前には数字か文字しか含まれていないため、その他の文字が見つかると終了の信号を出します。nextName() は、特別の注意を払って、読み取った最後の文字をバッファーに戻し、バッファーでは、その文字がnextValue() で使用できるようになります。
リスト2: nextName() の例
public String nextName()
throws SAXParseException
{
token.setLength(0);
int c = eatSpaces();
for(;;)
if(c == -1)
throw new SAXParseException(UNEXPECTED_EOS,locator);
// strictly speaking a name cannot start with a digit...
else if(!Character.isLetterOrDigit((char)c) && c != '-')
{
putc(); // put it back for the next call
return token.length() == 0 ? null : token.toString();
}
else
{
token.append((char)c);
c = getc();
}
}
|
nextValue() も同様な命令ですが、この命令は、まず、等しい文字 (nextName() によってバッファーに残された文字) と引用符を認識します。nextValue() は、定義済みエンティティー (<、>、など) もデコードします。
トークン処理プログラムを使用すれば、処理命令のデコードは簡単になります。ProcessingInstructionHandler から抜粋した以下のコードはxml-stylesheet を認識します。xm-xsl-param のコードは、以下のとおりです。
リスト3: ProcessingInstructionHandlerの抜粋
if(target.equals("xml-stylesheet"))
{
String href = null,
type = null;
boolean alternate = false;
PseudoAttributeTokenizer tokenizer =
new PseudoAttributeTokenizer(data,locator);
while(tokenizer.hasMoreTokens())
{
String name = tokenizer.nextName(),
value = tokenizer.nextValue();
if(name.equals("href"))
href = value;
else if(name.equals("alternate"))
alternate = value.equals("yes");
else if(name.equals("type"))
type = value.trim();
// ignore the media attribute...
}
if(type != null && href != null && !alternate &&
(type.equals("text/xsl") || type.equals("text/xml") ||
type.equals("application/xml+xslt")))
{
this.href = href;
params.clear();
readParams = true;
}
else
readParams = false;
}
|
XMが代替スタイル・シートを無視する点に留意してください。W3C勧告は、HTTPが、代替スタイル・シートに優先するデフォルトのスタイル・シートを提供するのを許可しています。XMは同じ規則を使用し、代替スタイル・シートを考慮することなく独自のデフォルト・スタイル・シートを適用します。
TemplatesManager
XMはもっと多くのスタイル・シートを使用しますので、キャッシング・ロジックが改良されています。これはTemplatesManager の仕事です。StylingMover がTemplates オブジェクトを要求する場合、キャッシュが使用可能であれば、そのオブジェクトはキャッシュから取り出されます。キャッシュが使用可能でなければ、TemplatesManager はスタイル・シートをロードし、それをキャッシュに入れます。TemplatesManager は、基本的には、Transformer オブジェクトを戻すための追加メソッドを持ったjava.util.Map のラッパーです。
既述のとおり、XMは文書とスタイル・シートを混合しません。反対に、文書ディレクトリーと規則ディレクトリーの2つのディレクトリーを使用します。TrAXは、XSLTプロセッサーによるファイル・ロードの方法を制御するためにURIResolver インターフェースを提供します。XSLTプロセッサーのURIResolver は、SAXパーサーのEntityResolver と似ています。つまりプロセッサーは、インポートしたスタイル・シート (xsl:import またはxsl:include 要素を介して) または文書 (document() 機能を介して) をロードするときに、そのresolve() メソッドを呼び出します。
TemplatesManager は、以下のように、規則ディレクトリーからスタイル・シートをロードする内部クラスReferenceResolver を使用します。
リスト4: ReferenceResolverの例
protected class ReferenceResolver
implements URIResolver
{
protected File rulesDir;
public ReferenceResolver(File rulesDir)
{
this.rulesDir = rulesDir;
}
public Source resolve(String href,String base)
{
if(href.endsWith(".xsl"))
{
File file = new File(rulesDir,href);
if(file.exists())
return new StreamSource(file);
}
return null;
}
} |
StylingMover
もちろん、私は新規クラスとしてStylingMover を使用しました。この機能は現在、ProcessingInstructionHandler ハンドラーを使って文書を構文解析します。リスト5 に示されているように、この機能は結果を使用してスタイル・シートを選択し、パラメーターを割り当てます。try/catchステートメントには特別の注意を払ってください。startElement() は特殊例外を使用して構文解析を停止しますので、それがエラーでないことをコードが認識している必要があります。
自動コンテンツ生成
これまでは、XMの作業に基本出版機能が関係していました。この機能は重要ですが、XMの本当の価値は、私が始めから念頭に入れている自動コンテンツ生成にあります。要するに、ユーザーに代わってXMにXML文書を生成させるのです。
たとえば、多くのWebサイトにはダウンロード・セクションが含まれています。ファイルのリストが頻繁に変わる場合は、いつも最新の状態になっているリストでXML文書を維持するのは大変です。ソフトウェアを使ってリストを自動的に生成するのが最善です。文書は、リスト6のようにすることができます。同様に、文書をSQLデータベースやメールボックスから生成することもできるし、リモートのWebサイトから生成することさえできるのです!
リスト6: XMから読み取ったディレクトリー
<?xml version="1.0" encoding="UTF-8"?>
<xm:Directory xmlns:xm="http://www.ananas.org/2001/XM/Walk/Directory">
<xm:File isDirectory="false" isFile="true" isHidden="false" canRead="true"
isMarked="false" lastModified="2001-07-07T18:21:10" canWrite="true"
length="749">NotImplementedException.java</xm:File>
<xm:File isDirectory="false" isFile="true" isHidden="false" canRead="true"
isMarked="false" lastModified="2001-07-20T11:49:42" canWrite="true"
length="6229">ContentHandlerExtractor.java</xm:File>
<xm:File isDirectory="false" isFile="true" isHidden="false" canRead="true"
isMarked="false" lastModified="2001-09-05T07:10:10" canWrite="true"
length="2351">JAXPHelper.java</xm:File>
</xm:Directory> |
先月のコラムでは、自動コンテンツ生成を追加するプロセスを単純化するMover を紹介しました。今月私は、ディレクトリー生成の予備バージョンをコードに組み込みましたが、来月は、それを再検討する予定です。それまで、興味があれば、DirectoryReader、WalkHandler、およびWalkMover を調べておいてください。
ユーザーの番
私は現在、XMを使った2つのWebサイト (ananas.orgおよび内部Webサイト) を保守しています。実際のXMの使用経験は、ソフトウェアを変更する方法を決定する場合に役立ちます。読者からの入力も喜んで拝見したいと思いますので、XMのコピーをダウンロードし、自分のサイトを構築するときにはそれを試行してください。その結果をananas-discussionメーリング・リストで連絡してくださるようお願いします (参考文献を参照)。
私はananas.org Webサイトのコード (.xml文書と .xslスタイル・シート) をCVSリポジトリーに追加しましたので、皆さんはそれを、自分のWebサイトを設計するための開始点にすることができます。
初期バージョンのXMがインストール済みであれば、今月の改良点を利用するためには、ご使用のソフトウェアを更新する必要があります。つまり、rules.xsl ファイルをdefault.xsl とリネームし、それをrules ディレクトリーに移します。これで、スタイル・シートを選択するための新しい基準が満たされます。
参考文献
- このプロジェクトのコードをananas.org からダウンロードすることができます。そこからdeveloperWorksのCVSリポジトリー へのリンク、さらにananas-discussionメーリング・リストへのリンクをたどってください。このリストに参加し、皆さんの考えをこのプロジェクトに入力してくださるようお願いします。
-
ZIPファイル を使用したい場合は、それもご利用いただけます。
- XMは、Xalan とXerces-J を、それぞれ、XSLTプロセッサーおよびXMLパーサーとして使用します。IBM (およびLotus) は、もともと、これらの両方を開発し、コードをApache Foundationに寄贈しました。
-
Compiler Construction from Niklaus Wirth (ISBN 0-2014-0353-6) は、構文解析について書かれた最も優れた入門書の1つです。180ページからなる読みやすい本です。
- developerWorksのXMLゾーン には、このほかにも多くのXML参考文献が含まれています。
著者について  | 
|  | Benoit Marchal氏は、ベルギーのナミュールを拠点にしたコンサルタントおよび著述家です。彼の著作には、 XML by Example(Que社、邦訳: インプレス社「実例で学ぶXML」。間もなく第2版が出版される予定です)、 Applied XML Solutions および XML and the Enterprise があります。また、Gamelanのコラムや、developerWorks XML zoneのコラムWorking XML の著者でもあります。最新プロジェクトの詳細については、www.marchal.com をご覧ください。 |
記事の評価
|