IDスペース

視覚表現をいくつかのZUMLページに分けるのはよくあることです。たとえば、注文購入のページ、支払いモーダルのダイアログ。すべてのコンポーネントが同じデスクトップで認識できるのなら、開発者は一つのデスクトップに追加されるすべてのページに対して、すべての認識詞を独特なものにしなければなりません。

IDスペースのコンセプトはこの問題を解決するため導入したものです。IDスペースはデスクトップコンポーネントのサブセットです。その唯一性はIDスペース内でのみ保障されています。

IDスペースのもっとも簡単な形はウィンドウ(org.zkoss.zul.Window)です。ひとつのウィンドウ(ウィンドウ自体を含む)との子コンポーネントコンポーネントは独立したIDスペースを形作ります。このようにして、ウィンドウをそれぞれのページの最上段のコンポーネントとして使えます。そうして開発者はそれぞれのページ別々に唯一性を保持する必要があります。

一般的に、org.zkoss.zk.ui.IdSpaceインターフェイスを実装する限り、どのコンポーネントもIDスペースを形成できます。ページはまたIdSpaceインターフェイスを実装するので、ページは空間所有者なのです。

最上段のIDスペースコンポーネントはIDスペースの所有者と呼ばれていて、コンポーネントインターフェイスの中からgetSpaceOwnerメソッドによってIDスペースを引き出すことができます。

IDスペース、例えばXは他のIDスペースの子で、X空間の所有者はY空間に含まれています。しかし、Xの子はY空間には含まれていません。

図に例示されたように三つの空間(P、A、C)がああります。P空間はPとAとFとGを含んでいます。A空間はAとBとCとDを含んでいます。C空間はCとEを含んでいます。

同じIDスペース中のコンポーネントはフェローと呼ばれます。たとえば、A・B・C・Dは同じIDスペースのフェローです。

ほかのフェローを引き出すのに、IdSapceインターフェイス、又は、コンポーネントインターフェイスの中のgetFellow メソッドを使います。

getFellowメソッドは同じIDスペースの最上段(所有者)に限らず、IDスペースのどのコンポーネントに対しても呼び出すことができます。同様に、空間所有者とは関係なく、getSpaceOwnerメソッドは同じIDスペースのどのコンポーネントに対しても同じオブジェクトを返します。

org.zkoss.zk.ui.PathクラスはIDスペース上のコンポーネントの位置を簡単化するためのユーティリティプログラムを提供します。その使用方法はjava.io.Fileと同様です。

Path.getComponent("/A/C/E");
new Path("A/C", "E").getComponent();

ネーム空間とIDスペース

インタプリタでコンポーネントに直接にアクセスするため、ネーム空間(org.zkoss.scripting.Namespace)のコンセプトを使用します。まず、どのIDスペースもネーム空間を持っています。さらに、ネーム空間の中で定義された値をネーム空間内のスクリプトコードとEL表記は参照できます。

<window border="normal">
    <label id="l" value="hi"/>    
    <zscript>    
        l.value = "Hi, namespace";        
    </zscript>    
    ${l.value}    
</window>

以下の例の中で二つのネーム空間があります。一つはウィンドウw1に属していて、もう一つはウィンドウw2 [22]に属している。このようにして、b1ボタンのonClickスクリプトはウィンドウw1で定義されたラベルを参照して、b2ボタンのonClickスクリプトはw2内で定義されたcheckboxを参照します。

<window id="w1">
    <window id="w2">    
        <label id="c"/>        
        <button id="b1" onClick="c.value = &quot;OK&quot;"/>        
    </window>    
    <checkbox id="c"/>    
    <button id="b2" onClick="c.label = &quot;OK&quot;"/>    
</window>

ここで一つ注意するポイントがあります:ネーム空間は階層です。つまり、ウィンドウw2内のzscriptw2の中に重複がない限り、ウィンドウw1のコンポーネントを見ることができます。このようにして、以下の例の中でボタンb1をクリックすることはラベルcを変えます。

<window id="w1">
    <window id="w2">    
        <button id="b1" onClick="c.value = &quot;OK&quot;"/>        
    </window>    
    <label id="c"/>    
</window>

ZKのコンポーネントはネーム空間にコンポーネントを指定する他に、setVariableメソッドによって変数をコンポーネントに指定することができます。そうしてzscriptはそれらを直接参照することができます。

zscriptの中で定義された変数とファンクション

コードを実行するに加えて、以下のように記述することで、変数と要素をzscriptメソッドの中で直接定義することができます。

<window id="A>
    <zscript>    
        Object myvar = new LinkedList();        
        void myfunc() {        
            ...            
        }        
    </zscript>    
    ...    
    <button label="add" onClick="myvar.add(some)"/>    
    <button label="some" onClick="myfunc()"/>    
</window>

zscriptの中で定義された変数とメソッドはスクリプト言語のインタプリタの中に保存されています。

zscriptとEL表記

ネーム空間[23]のように、zscriptの中で定義された変数をEL表記は参照できます

<window>
    <zscript>    
    String var = "abc";    
    self.setVariable("var2", "xyz", true);    
    </zscript>    
    ${var} ${var2}    
</window>

上記は以下と同様です。

<window>
abc xyz
</window>

zscriptの中で定義された変数はネーム空間で定義されたものより優先度が高いです。

<window>
    <zscript>    
    String var = "abc";    
    self.setVariable("var", "xyz", true);    
    </zscript>    
    ${var}    
</window>

上記は以下と同様です。

<window>
abc
</window>

以下の例のようにコンポーネントを宣言すると混乱を招くことがあります。

<window>
    <zscript>    
    String var = "abc";    
    </zscript>    
    <label id="var" value="A label"/>    
    ${var.value} <!-- Wrong! var is "abc", not the label -->    
</window>

混乱を避けるために、いくつかの名付け方式を使うことをお勧めします。たとえば、インタプリタ変数にはプレフィックスzs_をつけます。

加えて、できるだけローカル変数を使いましょう。ローカル変数はクラスの名前とともに定義され、zscriptコードの特定の範囲内のみに有効です。

<zscript>
Date now = new Date();
</zscript>

さらに、以下のように括弧でくくることでローカル変数をEL表記が参照できないように設定できます。

<zscript>
{ //create a new logic scope
   String var = "abc"; //visible only inside of the enclosing curly brace
}
</zscript>

多領域(マルチ・スコープ)インタプリタ

定義することによって、インタプリタはひとつの領域を持つか、変数とメソッドを保存する論理領域を持つことができます。区別するために、それぞれ、シングル領域と多領域インタプリタと呼びます。

Javaインタプリタ(BeamShell)は代表的な多領域インタプリタ[24]で、それぞれのIDスペースに独立のインタプリタ領域を作ります。例えば、以下の例で二つの論理領域はそれぞれウィンドウAとBのために確保されます。そうして、var2はウィンドウBのみに有効で、var1はAとBの両方のウィンドウに有効です。

<window id="A">
    <zscript>var1 = "abc";</zscript>    
    <window id="B">    
        <zscript>var2 = "def";</zscript>        
    </window>    
</window>
Javaインタプリタ(BeanShell)

Javaインタプリタ(BeanShell)を使って、以下のようにクラスの名前を定義することで一番近いIDスペースの論理領域(つまり、ウィンドウ)にローカルインタプリタ変数を宣言することができます。

<window id="A">
    <window id="B">    
        <zscript>        
    String b = "local to window B";    
        </zscript>        
    </window>    
</window>

以下はabcとdefを作成するより正確な例です。

<window id="A">
    <zscript>    
    var1 = var2 = "abc";    
    </zscript>    
    <window id="B">    
        <zscript>        
    Object var1 = "123";    
    var2 = "def";    
    var3 = "xyz";    
        </zscript>        
    </window>    
    ${var1} ${var2} ${var3}    
</window>

オブジェクトvar1=123は、クラス名とオブジェクトが指定されているので、ローカル変数をウィンドウBに定義しています。一方、オブジェクトvar2 = defはインタプリタに、現領域、又は上層領域(親など)内でvar2と定義された変数を参照させます。var2はウィンドウA中で定義されていて、変数はオーバーライドされています。var3=xyzの場合では、ウィンドウAがvar3と呼ばれる変数を定義していないので、ウィンドウBにローカル変数が指定されています。

単領域インタプリタ

Ruby,Groovy,JavaScriptインタプリタはまだ多領域をサポートしていません[25]。つまり、すべての変数はひとつの論理領域のみに定義されていることを意味しています。例えば、Rudyの変数は(インタプリタごとに)一領域中に保存されます。このように、同ページのであれば、ひとつのウィンドウ中に定義されたインタプリタ変数はほかのウィンドウでオーバーライドされます。混乱を避けるために、ウィンドウを意味する特別なプレフィックスのついた変数を書きます。

【ヒント】:どのページもzkscriptコードを処理するために自身のインタプリタを持っています。デスクトップは多数のページを持っていて、(スクリプト言語ごとに)インタプリタが複数のインスタンスを持っている場合があります。

一ページの中にある複数のスクリプト言語

どのスクリプト言語もひとつのインタプリタと繋がっています。変数とメソッドはひとつの言語の中で定義されていて、ほかの言語には使えません。例えば、以下の例の中でvar1とvar2は異なった2つのインタプリタに属しています。

<zscript language="Java">
    var1 = 123;    
</zscript>
<zscript language="JavaScript">
    var2 = 234;    
</zscript>

getVariablegetZScriptVariable

ネーム空間の中で定義された変数はgetVariableメソッドで引き出すことができます。

一方,zscript中で定義された変数はそれを翻訳するインタプリタの一部です。ネーム空間の一部ではありません。つまり、getVariableメソッドでそれらを取得することはできません。

<zscript>
    var1 = 123; //var1 belongs to the interpreter, not any namespace    
    page.getVariable("var1"); //returns null    
</zscript>

代わりにgetZScriptVariableをつかってzscript内で定義された変数を引き出さなければなりません。同様にして、getZScriptClassを使ってクラスを取得し、getZScriptMethodをつかってメソッドを取得します。これらのメソッドは見つかるまで、呼び出されたインタプリタを通して、繰り返されます。

特定のインタプリタを探す場合、以下のようにgetInterpreterメソッドを使って、インタプリタを引き出します。

page.getInterpreter("JavaScript").getVariable("some"); //interpreter for JavaScript
page.getInterpreter(null).getVariable("some"); //interpreter for default language


[22] ウィンドウはorg.zkoss.zk.ui.IdSpaceを実装しますので、独立したIDスペースとネーム空間が形成されます。

[23] org.zkoss.zk.scripting.Namespace

[24] 2.3.1(含む)以降および2.2.1(含む)以前、Javaインタプリタはマルチスコープに対応可能です。

[25] 今後サポートする予定です。