このセクションでは、スクリプトを使って RDF を操作する方法を説明します。
テンプレートを使えば、RDF データソースからデータを抽出し、その内容に基づいて内容を作ることが可能です。しかし、データソースは、スクリプトから検査することができます。これによって、データソースの変更ができます。これは、例えば、新しいブックマークを追加するのに必要となります。テンプレートを使って作られた要素からデータソースを捉え、個々のリソースを取り出すことができます。
RDF に対する XPCOM インターフェースには数多くのインターフェースがあります。以下のリストは、関連するインターフェースの一部です。
| nsIRDFService | グローバル RDF サービス。RDF データソース内のリソースを一意に識別できるリソースオブジェクトを生成するのに使われる。 |
| nsIRDFDataSource | 組み込みか、RDF ファイルの RDF データソース。メソッドを使って、値の取得と参照ができる。 |
| nsIRDFContainer | RDF データソース内のコンテナノード。メソッドを使って、リソースの追加と削除ができる。 |
| nsIRDFContainerUtils | このインターフェースには、Seq、Bag、Alt リソースを作るためのハンディーなコンテナメソッドが幾つかある。 |
「ファイル検索」ダイアログに、項目の最新の検索結果を保存する機能が実装できます。サーチテキストボックスを、最も最近検索された項目リストからなる編集可能なドロップダウンによって置き換えることができるでしょう。この機能は、今、付け加えることにしましょう。
これは実際には、ダイアログが、最新の検索項目リストが保存されているディスクの場所にアクセスできる場合にだけ動作します。このリストがある可能性が高いのは、ユーザーのプロファイルディレクトリーかユーザー自身が選んだディレクトリーでしょう。ここでは行ないませんが、ユーザーのプロファイルディレクトリーは、nsIFileLocator インターフェースを使って検索できます。例を簡単にするため、ファイルパスを直接 XUL の datasources 属性に置くだけにしましょう。
最新の検索リストはプレーンテキストファイルに保存することもできます。しかし、RDF を使うこともできます。データを読み書きし、テンプレートから作り出されたウィジェットを自動的に更新するための機能が既にあります。まず、XUL ファイルを変更します。テキストボックスをドロップダウンリストで置き換えます。datasources 属性の値を適切なパスで置き換えて下さい。(ファイル自身は存在する必要はありません。データが保存されるとき、それは自動的に作られます。)
<menulist id="find-text" flex="1" style="min-width: 15em;"
editable="true"
datasources="file:///mozilla/recents.rdf"
ref="urn:findfile:recent">
<template>
<menupopup>
<menuitem label="rdf:http://www.example.com/recent#Label" uri="rdf:*"/>
</menupopup>
</template>
</menulist>
|
テンプレートが作り出した子供をもつ XUL 要素はすべて、nsIRDFDataSource を参照する database プロパティーをもちます。このオブジェクトは、使われているデータソースを読んだり、それを更新するのに使うことができます。database プロパティーは、datasources 属性をもつ要素に置かれます。これは、多くの場合 tree か、この場合のように menulist 要素です。
database プロパティーには、datasources 属性内で指定されたデータソースそれぞれのリスト (実際には、nsISimpleEnumerator) があります。これは、たった一つしかない場合でも、個々の要素で処理を繰り返す必要があるということです。以下の例は、この方法を示しています。ここでは、データソースが一つしかないと想定しています。
var dsource;
var menulist=document.getElementById("find-text");
var sources=menulist.database.GetDataSources();
if (sources.hasMoreElements()){
dsource=sources.getNext();
}
dsource=dsource.QueryInterface(Components.interfaces.nsIRDFDataSource);
|
まず、メニューリストへの参照を取得します。ここでは、find-text という id があると仮定しています。次に、メニューリストからデータソースのリストを取得します。nsISimpleEnumerator インターフェースには、2 つのメソッドがあります (これは、Java の Enumeration インターフェースに似ています)。列挙 (enumeration) 内の要素をループし、getNext メソッドを使ってそれを取得します (一つしかないという前提です)。最後に、それが nsIRDFDataSource であることを確かめるために QueryInterface を呼び出します。
最新の検索リストを作るために、これと似たコードを使います。しかし、まず、使いたいコンポーネントを初期化することにしましょう。コンポーネントが 3 つ必要です。リソースオブジェクトを作るのに、インターフェース nsIRDFContainer が使えます。3 つ目のインターフェースである nsIRDFContainerUtils は、ルートノードを作るために、最新の検索リストが最初に使われるときにだけ使用します。スクリプトファイル (findfile.js) の先頭に、次のコードを追加します。これは、「ファイル検索」ダイアログがロードされる時に実行されます。
var RDFC = '@mozilla.org/rdf/container;1'; RDFC = Components.classes[RDFC].getService(); RDFC = RDFC.QueryInterface(Components.interfaces.nsIRDFContainer); var RDFCUtils = '@mozilla.org/rdf/container-utils;1'; RDFCUtils = Components.classes[RDFCUtils].getService(); RDFCUtils = RDFCUtils.QueryInterface(Components.interfaces.nsIRDFContainerUtils); var RDF = '@mozilla.org/rdf/rdf-service;1' RDF = Components.classes[RDF].getService(); RDF = RDF.QueryInterface(Components.interfaces.nsIRDFService); |
このコードは、使う必要のある 3 つのサービスを作ります。シンタックスは、XPCOM オブジェクトを作るためのコードと似ています。最初の 3 行は、nsIRDFContainer オブジェクトへの参照を取得します。次に、nsIRDFContainerUtils オブジェクトを取得するため、同じ操作を行ないます。最後に、再び、nsIRDFService を繰り返します。
次に、初期化関数を作ります。これは、ウィンドウの onload ハンドラーから呼び出します。ウィンドウが表示されるときに、これは実行されます。このコード内には、上で作った RDF オブジェクトを初期化するコードを追加することにします。
findfile.xul:
<window onload="initSearchList()" ... >
findfile.js:
function initSearchList()
{
var recentlist=document.getElementById("find-text");
var sources=recentlist.database.GetDataSources();
var rootnode=RDF.GetResource("urn:findfile:recent");
while (sources.hasMoreElements()){
try {
dsource=sources.getNext();
dsource=dsource.QueryInterface(Components.interfaces.nsIRDFDataSource);
RDFC.Init(dsource,rootnode);
return;
} catch (e){}
}
RDFCUtils.MakeSeq(dsource,rootnode);
RDFC.Init(dsource,rootnode);
}
|
initSearchList 関数を細かく見ていきましょう。
var recentlist=document.getElementById("find-text");
var sources=recentlist.database.GetDataSources();
まず、データソースをもつ menulist 要素への参照を取得する。そこには、存在するデータソースを保持する database がある。利用可能なデータソースへの参照を取得し、それを変数 resource に割り当てる。var rootnode=RDF.GetResource("urn:findfile:recent");
リソースオブジェクトは、与えられた URI を使って生成される。これは、ルートリソースとなり、最新の検索リスト内のそれぞれの項目を表わすリソースリストを保持する RDF Seq となる。この関数は、データソースから何も取得しない。それは、URI をリソース識別子に変換するだけである。URI をコードに埋め込む代りに、それを ref 属性から取得することもできるだろう。while (sources.hasMoreElements()){
try {
dsource=sources.getNext();
dsource=dsource.QueryInterface(Components.interfaces.nsIRDFDataSource);
次に、個々のデータソースをループして、適切なデータソースを取得する。RDFC.Init(dsource,rootnode);この関数は、データソースとルートノードをもつ RDF コンテナ (nsIRDFContainer インターフェース) を初期化する。後で、コンテナ内に新しいリソースを追加するため、コンテナオブジェクトを使うことができる。これは、検索した項目をデータソースに追加するために必要となる。データソースかルートノードが存在しない (例えば、RDF ファイルが見付からない場合) には、エラーが発生する。コードには、エラーを捉えるため、try-catch ブロックが入れてある。
return;例外が発生しなかった場合は戻るだけである。例外が発生した場合は、自分でルートノードを作る必要がある。
RDFCUtils.MakeSeq(dsource,rootnode); RDFC.Init(dsource,rootnode);return 文に行き着かなかった場合は、エラーが発生したということである。一番あり得るのはルートノードが存在しなかったということだろう。この場合は、それを捉えるため、nsIRDFContainerUtils インターフェースの MakeSeq メソッドを呼び出す。同様な関数が、bag と alt を作るためにも存在している (MakeBag と MakeAlt)。
インターフェース nsIRDFService には、引数として渡された文字列からリソースオブジェクトを作ってくれる GetResource というメソッドがあります。このメソッドは、他のものの値を取得することはありません。それは、文字列を、データソースから値を取得するのに使えるリソースオブジェクトに変換するだけです。RDF インターフェースを参照する場合、文字列ではなくリソースを使います。GetResource が返す値は、nsIRDFResource 型です。
オブジェクトの初期化ができたので、これでオブジェクトのリソースの追加と削除ができます。リソースをコンテナに追加したいのか、それともリソースを別のリソースに追加したいのか (アサーション (assertion) と呼ばれます) に応じて必要となる 2 つのメソッドがあります。これら 2 つのケースは、ブックマークを追加するのか、それともタイトルや URL のようなプロパティーをブックマークに追加するのかに対応しています。
ユーザーが Find ボタンをクリックしたら、項目リストの検索結果に新しいエントリーを追加することにしましょう。幾つかの意味で、これは単純化しすぎています。まず、重複したエントリーのチェックをしたくないからであり、次に、リストの長さを制限することに関わりたくないからです。
doFind 関数内部から呼び出される別の関数を追加しましょう。
function doFind()
{
var recentlist=document.getElementById("find-text");
var fldval=document.getAnonymousNodes(recentlist)[1].value;
addSearchedItem(fldval);
.
.
.
|
このコードは、メニューリストのテキストの値を取得します。通常は、このために 'recentlist.value' を使うだけですが、これはまだ実装されているとは思えません。このため、回り道をする必要があります。XBL のセクションで、このシンタックスが実際には何を意味しているのか説明します。テキストを、次に定義する関数 addSearchedItem に渡します。
function addSearchedItem(txt)
{
var newnode=RDF.GetResource("urn:findfile:recent:item"+(RDFC.GetCount()+1));
var labelprop=RDF.GetResource("http://www.example.com/recent#Label");
var newvalue=RDF.GetLiteral(txt);
dsource.Assert(newnode,labelprop,newvalue,true);
RDFC.InsertElementAt(newnode,1,true);
dsource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();
}
|
このコードは 3 つのことを行なっています。新しいリソースの追加、その値をもつ新しいアサーションの追加、変更されたデータソースの書き出しです。コードを細かく見ていきましょう。
var newnode=RDF.GetResource("urn:findfile:recent:item"+(RDFC.GetCount()+1));
この行は、追加しようとするリソースのためのリソースオブジェクトを作る。GetCount 関数は、コンテナ内に既にあるリソース数をカウントした結果を返す。これによって、一意な URI の生成ができる。(GetResource の代りに) GetAnonymousResource を呼び出すこともできる。これは引数を取らず、ランダムな一意の URI を生成する。var labelprop=RDF.GetResource("http://www.example.com/recent#Label");
最新の検索テキストに、リソースの Label プロパティーを設定する。プロパティー名 (と URL) は、それが一貫している限り、どんなものでも使うことができる。これが XUL ファイルの menuitem の label 属性と同じ値であることに気が付くだろう。var newvalue=RDF.GetLiteral(txt);GetLiteral 関数は、txt 引数により渡されたテキストの検索結果を内容とする RDF 文字列オブジェクトを作り出す。ここでは、値をリソースに割り当てるので、GetResource は使わない。
dsource.Assert(newnode,labelprop,newvalue,true);この行は、RDF データソースにアサーションを追加する。この場合、リソース 'urn:findfile:recent:itemX' の 'Label' は前の行で作ったリテラルオブジェクトであることを指示する。ここで、X とは、GetCount 関数が返した数である。しかし、これはしなければならないことの半分にすぎない。これ以外に、リソースが最新項目の一つであることを指示する必要がある。
RDFC.InsertElementAt(newnode,1,true);この行は、リソースをコンテナに追加する。ここでは、リソースを最初の位置に挿入する。(最初の要素は 1 であって、0 ではないことに注意。) リソースはどこにでも挿入できる。また、それを末尾に追加したい場合は、AppendElement を呼び出すこともできる。メニューリストテンプレートは新しいリソースを検知し、リストに行を追加する。
dsource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();最後に、Flush 関数を使ってデータソースをディスクに書き出す。この関数は nsIRDFDataSource インターフェースの一部ではない。そのため、データソースを適切なインターフェース nsIRDFRemoteDataSource に変換するため、まず、 QueryInterface を呼び出さなければならない。
| 必ずしもすべてのデータソースが更新できる訳ではありません。ファイルとリソース URL からロードされたすべてのデータソースは、内部データソースの幾つかと同様に出力できます。 |
「ファイル検索」ダイアログを開いて何かテキストを入力して Find を押すと、そのテキストがテキストドロップダウンの選択の一つになるのが分かるでしょう。作業を終了して再ロードしても、テキストはそのまま残ります。
重複したエントリーのチェックをするため、インターフェース nsIRDFDataSource の関数である hasAssertion や GetAllResources を使って既にあるリソースのチェックができます。
(進む) 次は、コピーと貼り付け操作のため、システムクリップボードにアクセスする方法を見ることにしましょう。