Oracle ADF UIX開発者ガイド | ![]() 目次 |
![]() 前へ |
![]() 次へ |
このトピックでは、ノード・ツリー自体は変更せずに、UIX XMLのデータ・バインディング属性および要素属性を使用して、ユーザーごとに動的コンテンツを生成するOracle ADF UIXページの作成方法を説明します。 UIXによるJavaBeansおよびデータをグループ化するためのMap
のサポート方法や、配列およびList
を持つデータの反復方法を説明します。 また、インライン・データをプロトタイプ用のUIX XMLに直接作成する方法も説明します。 次に、この機能をサポートするJava DataObject
、BoundValue
、DataProvider
およびDataObjectList
インタフェースや、これらのJava APIを使用してさらにカスタマイズされたデータ・バインディングを実装する方法を学習します。 最後に、JavaBeansに対するUIXサポートの詳細を説明します。
注意: このトピックでは、データ・バインドにEL(式言語)を使用しています。 ELは、UIXリリース2.2(JDeveloper 9.0.5に付属)で導入されました。現在では、UIXの以前のバージョンで使用されているdata:
構文の代替として推奨されています(data:
構文は現在のバージョンでもサポートされています)。 このトピックの内容は、以前のバージョンの『UIX開発者ガイド』のデータ・バインドのトピックと基本的に同じですが、data:
構文のかわりにEL構文を使用しています。 data:
構文をEL構文に変換する方法の詳細は、「data:
の変換: 式構文からEL構文」を参照してください。
ここでは、次の項目について説明します。
data:
の変換: 式構文からEL構文以降のいくつかのセクションでは、UIX XMLを使用する際のUIXのデータ・バインドについて説明します。 Java APIのみを使用してUIXの開発を行っている場合は、「Java APIからのデータ・バインド」へスキップしてもかまいません。
UIX 2.2(JDeveloper 9.0.5に付属)からは、UIXでは新しいELデータ・バインド構文と古いdata:
データ・バインド構文の両方がサポートされています。 このため、どちらの構文を使用するかを示す必要があります。
UIXページのルート要素が<ctrl:page ...>
、<ui:page ...>
または<ui:templateDefinition ...>
である場合、そのルート要素に属性expressionLanguage="el"
を設定します。 次に例を示します。
<templateDefinition xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:ui="http://xmlns.oracle.com/uix/ui"
expressionLanguage="el">
<content>
<!-- body -->
</content>
</templateDefinition>
ルート要素が前述以外のものである場合、次に示すとおり、<ui:page expressionLanguage="el">
要素内にラップします。
次のようなルート要素をラップします。
<dataScope ...>
<!-- body -->
</dataScope>
ラップ後は次のようになります。
<page xmlns="http://xmlns.oracle.com/uix/ui" expressionLanguage="el">
<content>
<dataScope ...>
<!-- body -->
</dataScope>
</content>
</page>
data:
に設定する方法構文を明示的にELに設定しない場合、UIXでは、デフォルトで古いdata:
構文を使用するとみなされます。 属性expressionLanguage="data"
を使用して、data:
構文を明示的に設定することもできます。
アプリケーションのデフォルトのバインド構文は、uix-config.xml
構成ファイルに設定できます。 これを行うには、<application-configuration>
要素の子である<default-expression-language>
の値を設定します。 デフォルトをELに設定するには、値にel
を使用します。 デフォルトをdata:
に設定するには、値にdata
を使用します。 次に例を示します。
<default-expression-language>el</default-expression-language>
値を設定しない場合、アプリケーションによってデフォルトでdata
が使用されます。 構成およびuix-config.xml
ファイルの詳細は、「ADF UIXの構成」のトピックを参照してください。
前のトピックの一番初めの例を使用して、最も単純な例から説明します。
<text xmlns="http://xmlns.oracle.com/uix/ui"
text="hello, world"/>
「hello, world」のデータ・バインドのサポートを、ある興味深い内容に追加します。 具体的には、非常に単純なJavaBeansを使用して、現在の日付と時刻を表示します。
package yourpackage;
import java.util.Date;
public class CurrentDateBean
{
public CurrentDateBean() { }
public String getTime()
{
return (new Date()).toString();
}
}
ここで、getTime()
が使用できるようにページを変更します。 次の3つの作業が必要です。
text
属性をデータ・バインドします。<dataScope>
をページに追加し、コンテンツにデータを供給します。まず、textをデータ・バインドします。
<text xmlns="http://xmlns.oracle.com/uix/ui"
text="${uix.data.currentDate.time}"/>
この例に、1点の小規模な変更が加えられました。 値が、データを定義する式である${uix.data.currentDate.time}
に変更されています。 これは、time
プロパティをcurrentDate
から取得することを意味します。
このサンプル・コードを実行しても、何も起こりません。 currentDate
をページに設定していないため、データ・バインドに失敗し、textがNULLのままだからです。 この設定をするには、<dataScope>をページに追加します。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<provider>
<data name="currentDate">
<method class="yourpackage.DataDemo" method="getCurrentDate"/>
</data>
</provider>
<contents>
<text text="${uix.data.currentDate.time}"/>
</contents>
</dataScope>
これで、大幅な変更が加えられました。 結果は次のとおりです。
<dataScope>
には、通常、すべてのデータ・プロバイダをグループ化する<provider>
、およびUIコンテンツをグループ化する<contents>
の2つの子要素があります。<provider>
には、任意の数の<data>
要素を含めることができます。各<data>要素によって、データ・プロバイダが指定されます。currentDate
1つのみです。これはyourpackage.DataDemo
クラスのgetCurrentDate()
メソッドを使用します。 <data>
に組み込まれる要素は多数ありますが、ここでは<method>
のみ使用します。このページに入力してこれを実行した場合、yourpackage.DataDemo
クラスがUIXによって検出されないとエラーが表示されます。 その際、このクラスを記述する必要があります。
package yourpackage;
import oracle.cabo.ui.RenderingContext;
public class DataDemo
{
static public Object getCurrentDate(
RenderingContext context, String namespace, String name)
{
return new CurrentDateBean();
}
}
これは、単純なクラスです。 UIX XMLの<method>
要素によって参照されると記述された、すべてのJavaのデータ・プロバイダには、ここで確認できるものと同一のシグネチャが設定されている必要があります。 このサンプル・コードにある3つのパラメータはいずれも必要ではありませんが、このJavaBeansを返す必要があります。 このメソッドをCurrentDateBean
クラスに移動することも可能ですが、このコーディング・スタイルはお薦めしません。 JavaBeansはどのアーキテクチャでも再利用可能なように設計する必要があります。さらに、UIX、サーブレットまたは他のテクノロジへの依存をできるだけ排除する必要があります。
先へ進む前に、2つの疑問点を解決しましょう。
DataDemo.getCurrentDate()
がコールされるタイミング
DataDemo.getCurrentDate()
は、ページがレンダリングされるたびに1回コールされます。 CurrentDateBean
に複数のプロパティが設定されていて、そのすべてを使用した場合でも、Beanは1回ロードされます。 ただし、getCurrentDate()
はページがレンダリングされるたびにコールされます。 ページを100回表示する必要がある場合、このメソッドは100回コールされます。
CurrentDateBean.getTime()
がコールされるタイミング
CurrentDateBean.getTime()
は最低1回コールされます。 UIXでは、これによって各プロパティが1回しか要求されないとはかぎりません。繰り返し要求される場合もあります。 通常getTime
によって新しいDate
オブジェクトが作成されるので、ページでtimeを何度も使用すると、異なる時刻がページに表示される可能性があります。 これが問題であるならば、Date
をCurrentDateBean
コンストラクタに作成し、getTime()
がコールされるたびに同じオブジェクトを再利用してください。
JavaBeansは、データをカプセル化する適切な方法です。ただし、UIXで状態を設定するたびに新しいJavaBeansクラスを記述したり、新しいプロパティが使用されるたびに新しいget
メソッドを追加することは、単調で時間を要します。 また、すべてのプロパティ名を事前に把握するのは不可能な場合もあります。その場合は実行時に検出するしかありません。 UIXでは、このような事態に対応するために、Java Map
インタフェースがサポートされています。
次に示すのは、JavaBeansのかわりにマップを使用するデータ・プロバイダです。
package yourpackage;
import oracle.cabo.ui.RenderingContext;
import java.util.HashMap;
public class DataDemo
{
static public Object getURLAndText(
RenderingContext context, String namespace, String name)
{
HashMap map = new HashMap();
map.put("text", "Oracle Corporation");
map.put("url", "http://www.oracle.com");
return map;
}
}
さらに、これを使用するUIXを次に示します。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<provider>
<data name="link">
<method class="yourpackage.DataDemo" method="getURLAndText"/>
</data>
</provider>
<contents>
<text text="${uix.data.link.text}"
destination="${uix.data.link.url}" />
</contents>
</dataScope>
この方法で開発するのは簡単ですが、JavaBeansを使用する場合ほど適切ではありません。 いつどの方法を使用するかは個人の好みの問題であり、ほとんどの開発者は両方の手法を使用することになるでしょう。
<boundAttribute>
要素最初の例を少し拡張してみましょう。 単に時刻を表示するかわりに、時刻の前に「The time is:」と表示します。 この静的テキストが含まれた<text>
要素を追加する方法もありますが、ここでは異なる手法を示します。 ${...}
を使用する場合、JavaBeanから値を取得し、その値に単純な操作を行うことができます。 <boundAttribute>
要素によって、高度な設計ができます。 簡単な連結法則を使用した、非常に複雑な式が可能です。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui">
<provider>
<data name="currentDate">
<method class="yourpackage.DataDemo" method="getCurrentDate"/>
</data>
</provider>
<contents>
<text>
<boundAttribute name="text">
<concat>
The time is:
<dataObject select="time" source="currentDate"/>
</concat>
</boundAttribute>
</text>
</contents>
</dataScope>
上の例から変更された部分は<text>
要素のみです。 ${...}を使用してtextをバインドするかわりに、<boundAttribute>
を使用しています。 これには<concat>
要素が含まれており、これによってBeanから(<dataObject>
要素とともに取得された)timeが静的文字列に結合されます。
<boundAttribute>
内で使用される要素は数十個ありますが、ここではすべてを説明しません。 これらの要素の詳細は、JDeveloperのUIXビジュアル・エディタにある「コード・インサイト」を使用するか、「UIX要素リファレンス」を参照してください。 これらの要素は、対応するJavaインタフェースに由来してBoundValue
要素と呼ばれます。 他のいくつかのBoundValue
要素には、次のものが含まれます。
<defaulting>
: デフォルト値を提供<dateFormat>
: Dateを文字列に書式化<if>
、<and>
、<or>
、<not>
: ブール論理を提供<messageFormat>
: 文字列に代替パラメータを挿入これらの大半については後半のトピックで説明しますが、全リストは「要素リファレンス」を参照してください。
ここまでで、動的データを含むページを設定する方法を学びました。 ただし、前述のインタフェースでは、順序付けられていない一連のプロパティがサポートされるだけです。 表形式のデータ、またはJava配列のようなその他のデータを表示する場合、これは非常に不便なインタフェースです。 UIXでは、List
およびJava配列の反復をサポートしています。 また、UIX固有のAPIであるDataObjectList
もサポートしています。これについては後述します。
ここで、Java配列およびList
からデータを取り出して、ページに戻す方法について説明します。 それには、現在のDataObject
をコールする設定が必要です。 すべてのデータ・フレームワークで、データの反復処理がサポートされています。 これはJavaではfor
ループに相当します。 データベースではカーソルに相当します。 UIX Componentsでは、現在のDataObject
の指定がそれに当たります。 現在のDataObject
からデータを取得するために、ソース全体を省略するか、uix.current
暗黙オブジェクトを使用します。
<styledText>
<boundAttribute name="text">
<dataObject select="time"/>
</boundAttribute>
</styledText>
<!-- OR -->
<styledText text="${uix.current.time}"/>
<dataScope>
のcurrentData
属性を使用して、現在のDataObject
を明示的に設定できます。
<dataScope currentData="${uix.data.currentDate}">
<!-- Now we have a current data object -->
...
<styledText text="${uix.current.time}"/>
...
</dataScope>
... しかし、これは通常使用される方法ではありません。 さらに一般的な方法として、List
または配列を自動的に反復し、現在のDataObject
を自動設定するコンポーネントを使用します。 UIXの<table>は、このようなコンポーネントの1つです。 ここでは<table>の詳細は省略します。<table>は、1つのトピックを設ける必要があるほど多機能だからです。ここでの説明は、現在のDataObject
の機能を理解するためのみにとどめておきます。
最初に、表に対してList
を作成します。 ここで、Map
のArrayList
を使用します。 Map
の配列やJavaBeans
の配列なども、同じくらい簡単に使用できます。
package yourpackage;
//...
import java.util.List;
import java.util.ArrayList;
public class DataDemo
{
// ...
static public Object getTableData(
RenderingContext context, String namespace, String name)
{
// The following List is recreated every time this method is called
// (which is once per page render). A better implementation would
// move the List creation to a UIX Servlet Event Handler.
// See the Using a Controller in ADF UIX chapter for more information.
List list = new ArrayList();
for (int i = 0; i < 5; i++)
{
HashMap row = new HashMap();
row.put("text", "Row " + i);
row.put("text2", "Column 2, row " + i);
list.add(row);
}
return list;
}
}
次に、データを<table>
に格納します。 <table>
の各列は、<contents>
の単一の子によって表されています。 このような子が3つある場合、表には列が3つ設定されます。
異なる方法でレンダリングされる各子を行ごとに取得する点が特長です。 これは、現在のDataObject
の指定を使用することで可能になります。 表で最初の行をレンダリングする際、現在のDataObject
により、List
の最初のMap
がポイントされます。 2行目のレンダリング時には、現在のDataObject
により、List
の2番目のMap
がポイントされます。 以下同じ処理が繰り返されます。
ここで表に2つの列を追加します。1つは通常のテキスト列、もう1つは赤色の列です。 (この例では、ビルトインのUIX Stylesの1つである、OraErrorText
スタイルを使用しています。)
<dataScope>
<provider>
<data name="someTableData">
<method class="yourpackage.DataDemo" method="getTableData"/>
</data>
</provider>
<contents>
<table tableData="${uix.data.someTableData['.']}">
<contents>
<styledText text="${uix.current.text}"/>
<styledText styleClass="OraErrorText" text="${uix.current.text2}"/>
</contents>
</table>
</contents>
</dataScope>
これですべてです。 コードはそれほど長くありませんが、利点は多いです。
まず、<table>要素は、データ・バインドされたtableData
属性を使用してデータを取得しています。 ここで注目すべき点は、データ・バインドの式にあるピリオド(.)です。 先に記述したgetTableData()
では、ArrayList
が返されました。 表のデータには、ArrayList
のプロパティではなく、ArrayList
そのものが必要です。 この構文によって、それが可能です。
次に、2つの子が追加されました。 1つ目は、現在のDataObjectのtext
キーからテキストを取得する<styledText>
です。 2つ目は、別のキーにバインドされたテキストが設定された、もう1つの<styledText>
です。 データ・モデルをまったく使用せずに、列の追加、削除およびレンダリングをUIで直接行えます。
これは単純な例です。列ヘッダーや行ヘッダー、あるいは表で実行可能な多数の処理は追加していません。また、編集不可の普通のテキストのみを使用しています。 ただし、これらはすべて同じ方法で機能します。 表の行ごとにレンダリング方法を変える場合、現在指定されているDataObject
に、その変更内容を示す情報が指定されている必要があります。
たとえば、各行にリンクを指定し、それぞれの宛先が異なるとします。 この場合は簡単です。 <link>
要素を使用してdestination
属性を現在指定されているDataObject
にデータ・バインドし、各行の宛先を含むようにDataObject
を更新します。 UIXでは、すべてについてデータ・バインドが可能なため、各行が異なるようにできます。
DataObject
APIDataObject
Javaインタフェースは、UIX ComponentsおよびUIX XMLのページの汎用データソースです。 UIXでは、JavaBeansやマップを渡す場合、軽量アダプタ・クラスを使用して、これらのオブジェクトをDataObject
に変換します。 このアダプタについては後述します。
ここでは、DataObject
そのもののJava APIについて、およびこれが関心を持たれる理由を説明します。 DataObject
は、1つのメソッドのみを含む、非常に単純な新たなインタフェースです。
public interface oracle.cabo.ui.data.DataObject
{
/**
* Select a single value out of the DataObject.
* @param context the current RenderingContext
* @param select a select key
* @return the selected value
*/
public Object selectValue(RenderingContext context, Object select);
}
DataObject
は小規模のインタフェースであり、開発者またはデータ構造に対する要件はごくわずかです。 そのため、次のような任意のデータ形式に容易に適応できます。
UIXには、前述のJavaBeansおよびマップへのビルトイン・バインドが組み込まれています。 その他の多くのデータ・ソースも、ADFモデルを使用してUIXで使用できます。
ほとんどの開発者は、最初にselect
パラメータを目にした際にHashtable
のキーを想定するでしょうが、DataObject
はそのようには機能しません。 select
パラメータは次のいずれかになります。
Integer
インデックスUIXによる開発では多くの場合、DataObject
APIに対してコード化する必要はありません。 しかし、マップやJavaBeansのかわりにDataObject
を使用することを考慮してもいい理由があります。
DataObject
はUIXでデータ用に使用される実際の概念です。 したがって、Java APIを使用してBeansを作成する場合、メソッドの取得対象であるDataObject
を使用する必要があります。 (後述するとおり、BeanAdapterUtils
を使用してJavaBeansおよびMapをDataObject
に簡単に変換することもできます。)
また、これらのJavaBeansに対するビルトイン・サポートによるオーバーヘッドがまったくないわけではありません。 パフォーマンス上のコストがかかります。 最初に、DataObject
を実装するアダプタ・オブジェクトを作成する必要があります。 これらは軽量であるとは言え、オブジェクト作成は可能ならば避けた方が無難です。 次に、イントロスペクション(実行時におけるメソッドの動的な検出およびコール)は本質的に動作が遅くなります。 このプロセスの最もコストが高い部分(Method
オブジェクトの検出)のキャッシュについては快適な動作が提供されますが、Method
オブジェクトへのコールについては依然として、手動コーディングされたDataObject
におけるコンパイル済メソッド・コールよりも動作が遅くなります。
ただし、「早まった最適化は諸悪の根源である(Premature optimization is the root of all evil)」というプログラミングについての有名な格言を思い出してください。 手動コーディングされたDataObject
がより速いとしても、DataObject
の記述に時間を費やすべきということにはなりません。 適度に使用されていれば、JavaBeansの使用がパフォーマンスに重大な影響を与える理由はありません。 本質的に動作が遅いJavaBeans(データベースにアクセスする必要がある場合など)については、アダプタの追加オーバーヘッドは少なくほとんど問題になりません。 ただしプロファイリングを実行して、Beansの1つの適合オーバーヘッドがパフォーマンスに重大な影響を与えていると判断した場合は、オブジェクト作成およびイントロスペクション・オーバーヘッドの両方の問題に取り組むことができます。 このパフォーマンスに関する問題を解決するいくつかの簡単な方法は、後で説明します。
最後に、そして最も有用な点ですが、DataObject
はRenderingContext
を受け取ります。 RenderingContext
には多くの有用な値が含まれています。 Locale
を使用してdate文字列を適切に書式化する例を示します。
package yourpackage;
import oracle.cabo.ui.RenderingContext;
import oracle.cabo.ui.data.DataObject;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
public class CurrentDateObject implements DataObject
{
public Object selectValue(RenderingContext context, Object select)
{
if (!"time".equals(select))
return null;
Locale locale = context.getLocaleContext().getLocale();
DateFormat formatter = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.MEDIUM, locale);
return formatter.format(new Date());
}
}
CurrentDateBean
のかわりにこのクラスを使用する場合、日付は現在のLocale
に対して自動的に正しく書式化されます。
UIX XML内にデータとデータ・リストの両方を直接定義できます。 ページが実際のデータソースに連結される前にダミー・データを作成できるため、非常に便利です。
まず、DataProvider
の新しい要素、<inline>
を紹介します。
<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
xmlns:demo="http://www.example.org/">
<provider>
<data name="linkData">
<inline>
.... data object is in here ...
</inline>
</data>
</provider>
<contents>
...
</contents>
</dataScope>
この要素は、前述した<method>
要素に似た動作をしますが、Javaコードはコールしません。 UIX XML内でデータを直接定義します。
<inline>
のすべての属性によって、作成されるデータ内にキーと値のペアが定義されます。 次に例を示します。
<inline url="http://example" text="Inline text">
</inline>
上の例では、2つの値を持つデータを定義しています。1つのキーはurl
、もう1つのキーはtext
です。 簡単に定義できます。
ただし、子要素を使用し、サブ値としてデータおよびデータ・リストを追加することもできます。 要素の名前は、子のデータまたはデータ・リストが格納されるキーです。 同じ要素名が複数ある場合は、それらの要素がまとまってデータ・リストを定義します。 1つしかない場合、1つのデータの集合と単一要素のデータ・リストの両方が定義されます。 次の例でわかりやすく示します。
<inline foo="Inline text">
<stuff random="Stuff text"/>
<row text="First row"/>
<row text="Second row"/>
</inline>
この例には、3つの内容を含むデータがあります。
foo
の文字列
"Inline Text"。stuff
の1つのデータの集合。キーrandom
のテキストStuff textを含みます。ただし、これは単一要素のデータ・リストとしても使用されます。row
のデータ・リスト。それぞれ1つのテキストを含む、2つのデータの集合を含みます。非常に簡単です。 ここで表の例に戻ります。今回は、インライン・データのみを使用します。
<dataScope>
<provider>
<data name="inlineData">
<inline>
<tableKey text="Row 0" text2="Column 2, Row 0"/>
<tableKey text="Row 1" text2="Column 2, Row 1"/>
... etc ...
<tableKey text="Row 4" text2="Column 2, Row 4"/>
</inline>
</data>
</provider>
<contents>
<table tableData="${uix.data.inlineData.tableKey}">
<contents>
<styledText text="${uix.current.text}"/>
<styledText styleClass="OraErrorText" text="${uix.current.text2}"/>
</contents>
</table>
</contents>
</dataScope>
このセクションでは、キーとデータ・オブジェクトをネストして、さらに複雑なデータ・バインドを生成する方法を説明します。 最初に、次のフォームを使用して、ネストされたキー・データ・バインドを生成します。
attributeName="${uix.data.name.key1.key2...keyN}"
この場合、この属性の値を計算する際に、name
で参照されるDataObject
についてkey1
が使用され、別のDataObject
が生成されます。2番目のDataObject
にはkey2
が使用され、3番目のDataObject
が生成されます。 このプロセスは、keyN
が使用され、この属性の最終値が生成されるまで続きます。 すべてのDataObject
の値自体がDataObject
であることが必要です。最後のキーkeyN
の値のみは、(属性が必要とするタイプであれば)DataObject
でなくてもかまいません。 ネストされたキー・バインドの例を次に示します。
<dataScope>
<provider>
<data name="families">
<inline>
<Smith members="4">
<address number="2255" street="37th Ave" city="San Francisco"
state="CA" zip="94116" />
</Smith>
<Jones members="3">
<address number="500" street="Oracle Parkway" city="Redwood Shores"
state="CA" zip="94065" />
</Jones>
</inline>
</data>
</provider>
<contents>
<messageTextInput prompt="The Smiths live in "
text="${uix.data.families.Smith.address.city}" /> <!--San Francisco-->
<messageTextInput prompt="The Jones's live in "
text="${uix.data.families.Jones.address.city}" /> <!--Redwood Shores-->
</contents>
</dataScope>
上の例では、最初のmessageTextInput
で「The Smiths live in San Francisco」が生成され、2番目で「The Jones's live in Redwood Shores」が生成されます。
ネストされたデータ・オブジェクト・バインドを生成するために大カッコも使用できます。 次の例について考えてみます。
attributeName="${uix.data.name2[uix.data.name1.key1]}"
上の例では、name1
(で参照されるDataObject
)でkey1
が使用されてキーを生成し、そのキーがname2
で使用されてこの属性の最終値を生成しています。 これは、キー自体がデータ・バインドされる例です。 .key
は、['key']
の簡易表記法です。
<!-- These two are equivalent -->
attributeName="${uix.data.name.key1.key2}"
attributeName="${uix.data.name.key1['key2']}"
次に示すのは、前の例で作成されたfamilies
データ構造を使用した大カッコの例です。これによって、「The Jones family has 3 members.」が生成されます。
<dataScope>
<provider>
<data name="families">
... as above ...
</data>
<data name="selection">
<inline current="Jones" />
</data>
</provider>
<contents>
The <link text="${uix.data.selection.current}"/> family
has <link text="uix.data.families[uix.data.selection.current].members" /> members.
</contents>
</dataScope>
大カッコは、次の例のようにネストすることもできます。
attributeName="${uix.data.name3[uix.data.name2[uix.data.name1.key1]]}"
この例では、key1
がname1
で使用されてキーを生成し、そのキーがname2
で使用され、最終値を計算するname3
に対する別のキーを生成します。
キーを省略すると、次の例のようにDataObject
自体が返されます。
<!-- The following attribute is bound to the data object itself -->
attributeName="${uix.data.name}"
キーおよびデータ・オブジェクト名には一定の制限があります。たとえば、キーのテキストには[記号を使用できません。 ただし、uix.data.name["["]
という大カッコ形式を使用すると、キー内でこの記号を使用できます。
このセクションでは、UIX Componentsのデータ・プロバイダによって実装される暗黙変数を示します。
uix
oracle.cabo.ui.expl.UIImplicitObject
のインスタンスです。 uix
は多くの関数で使用されます。 RenderingContext
情報を取得するために有効です。
uix.data
<dataScope>
<provider>
<data name="someData">
<inline textKey="Demo text"/>
</data>
</provider>
<contents>
<styledText text="${uix.data.someData.textKey}"/>
</contents>
</dataScope>
uix.current
DataObject
から値を取得するために使用します。
uix.rootAttr
uix.helpTopic
<link text="Table Doc"
destination="${uix.helpTopic.ui_table}"/>
uix.helpSystem
<link text="Main Help Page"
destination="${uix.helpSystem.frontPage}"/>
requestScope
sessionScope
<messageTextInput prompt="User Name"
text="${sessionScope.username}"/>
applicationScope
param
paramValues
header
headerValues
cookie
initParam
UIXファイルで使用可能なUIX Componentsの関数を次に示します。
ui:encodeParameter
(UIImplicitObject uix, String parameterName)
<formValue name="${ui:encodeParameter(uix,'event')}"
value="create" />
ui:transformId
(UIImplicitObject uix, String idText)
<image id="${ui:transformId(uix,'statusIcon')}" .../>
ui:defaulting
(Object arg1, Object arg2)
ui:cond
(boolean test, Object ifTrue, Object ifFalse)
test
がtrueの場合はifTrue
を返します。trueではない場合はifFalse
を返します。 次に例を示します。
<messageTextInput prompt="Age" text="${uix.current.age}"
message="${ui:cond(uix.current.age > 100, 'Wow', null)}"/>
ui:hyphenate
(UIImplicitObject uix, String text, String breakChars)
breakChars
に含まれる文字が出現するたびに、ソフト・ハイフン文字をtext
に挿入します。 ソフト・ハイフンは、テキストがその時点で破損している可能性があることをブラウザに示します。
次に例を示します。
<text text="${ui:hyphenate(uix, '/src/oracle/cabo/ui/expl/UIFunctions.java', '/')}"/>
ui:concat
(String str1, String str2)
<text text="${ui:concat('Julius','Caesar')}"/>
prefix:data
()
<dataScope xmlns:demo="http://demo">
<provider>
<data name="demo:test">
<inline fooKey="bar"/>
</data>
</provider>
<contents>
<!-- text is bound to "bar" -->
<styledText text="${demo:data().test.fooKey"/>
</contents>
</dataScope>
次のいくつかのセクションで、Java API固有の多くの詳細を説明します。 全体をUIX XMLから開発している場合、独自に作成したUIX XMLの機能およびフレームワークの拡張方法を取得するとき、これは重要になる可能性があります。
BoundValue
BoundValue
インタフェースは、UIX Componentsの動的ページにとって不可欠です。 これは、次のようにRenderingContext
が渡され、任意のJavaオブジェクトが返される1つのメソッドで構成されています。
public interface oracle.cabo.ui.data.BoundValue
{
public Object getValue(RenderingContext context);
}
この単純なインタフェースが便利なのは、すべてのUIX Components Beanであらゆる属性値のかわりに使用できるためです。 属性の値がBoundValueに設定されている場合、その属性の値を取得しようとしてもBoundValueは返されません。 UIX ComponentsではBoundValue.getValue()
をコールし、結果を返します。 次に例を示します。
// Remember the code to set the text of a StyledTextBean...
styledTextBean.setText("Some text");
// ... is really a cover for:
styledTextBean.setAttributeValue(UIConstants.TEXT_ATTR,
"Some text");
// So you can also set a BoundValue:
BoundValue boundValue = ...;
styledTextBean.setAttributeValue(UIConstants.TEXT_ATTR,
boundValue);
この後、表示時にStyledTextBeanのテキストを要求する場合、次のように指定します。
text = styledTextBean.getAttributeValue(renderingContext,
UIConstants.TEXT_ATTR)
// .. is equivalent to writing:
BoundValue boundValue = ...;
text = boundValue.getValue(renderingContext);
setAttributeValue()
でBoundValue
を使用して、すべての属性をデータ・バインドできることを覚えておいてください。 属性のデータ・バインドのための専用のメソッドがそのBeanにあるかどうか、以前にその属性がデータ・バインドされたかどうか、あるいはその属性が必要とするJavaオブジェクトのタイプはどれかなどは関係ありません。 すべての属性をデータ・バインドできます。
多くのBeanで、属性をバインドするための便利なメソッドが用意されています。 StyledTextBean
のtext
属性もその1つです。 次のように記述できます。
BoundValue boundValue = ...;
styledTextBean.setTextBinding(boundValue);
ただし、これは利便性が高いというだけです。 便利なメソッドがあるかどうかにかかわらず、すべてのBeanですべての属性をバインドできます。
BoundValue
には多数の実装が用意されていますが、次にカスタムの実装について説明します。 次のコードでは常に現在の日付が返されます。
public class CurrentDate implements BoundValue
{
public Object getValue(RenderingContext context)
{
return new Date();
}
}
これをDateFieldBean
で使用します。
DateFieldBean dateField = new DateFieldBean();
dateField.setValueBinding(new CurrentDate());
上の例で生成される日付フィールドは、常に現在の日付で初期化されます。 上のコードは次のコードとよく似ています。
DateFieldBean dateField = new DateFieldBean();
dateField.setValue(new Date());
ただし大きな違いがあります。 2番目の例では日付の設定は1回です。 Beanを1回のみ使用して解放する場合は、問題ありません。 ただし、UIX ComponentsではBeanは何度も再利用できます。 最初の例で使用したBoundValue
の場合、UIX Componentsにより、ページのレンダリングのたびに日付が要求されます。このため、DateFieldBean
が作成された後でも常に正確な月日が表示されます。
次に、独自のDataObject
クラスの記述方法を説明します。
BundleDataObject
このクラスでは、JavaのResourceBundle
をDataObject
に変換します。 このクラスは大変便利なため、UIX Componentsに含まれています。ここではその説明を通して、DataObject
を実装する際の基本的な考え方を示します。 この例では、DataObject
で例外を処理する方法の1つも示されます(他の方法は「ADF UIXでのエラーの処理」のトピックを参照してください)。
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import oracle.cabo.ui.data.DataObject;
public class BundleDataObject implements DataObject
{
public BundleDataObject(ResourceBundle bundle)
{
_bundle = bundle;
}
public Object selectValue(RenderingContext context, Object select)
{
try
{
return _bundle.getObject(select.toString());
}
catch (MissingResourceException e)
{
context.getErrorLog().logError(e);
return null;
}
}
private ResourceBundle _bundle;
}
留意点:
ResourceBundle
をラップした場合、1つのオブジェクトしか割り当てられません。 しかし、Map
を作成してすべてのデータをResourceBundle
から取り出した場合、さらに多数のオブジェクトを作成する必要があります。 また、前述の例の場合は、実行時に実際に使用されているキーのみが取得されます。DataObject
で、例外がスローされないようにする必要があります。 ここでは、MissingResourceExceptions
を検出し、標準のUIX ErrorLog
に記録してから、安全な値すなわちnullが返されています。DataObject
の独自の実装は、非常に簡単に記述できます。 カスタムの実装を使用することによって、アプリケーションの効率やパフォーマンスが向上します。
DataObject
とRenderingContext
DataObject
の作成後、RenderingContext
インタフェースに連結して、UIX Componentsのページで使用できるようにします。 (前述のように、このインタフェースが、UIX Components BeanとUIX XMLのページに、レンダリングに必要なコンテキストを渡します)。 ここで、RenderingContext
の特定のメソッドに注目します。
public DataObject getDataObject(String namespaceURI,
String localName)
このメソッドによって、UIX Components BeanまたはBoundValue
では、任意の数のDataObject
へのアクセスが可能になります。 UIXでは通常、これらのDataObject
は2つの文字列で識別されます。 WebサイトのURLに基づく1つ以上のネームスペースと、 他のコードと競合しない任意のローカル名を使用します。
DataObject
へのバインド: DataBoundValue
ここで、DataObject
をRenderingContext
に渡す方法からは一度離れ、DataObject
からデータを取得してUINode
またはUIXに渡す方法を説明します。 ここで、DataBoundValue
クラスが渡されます。UIX Componentsでは、DataBoundValue
クラスを使用して、RenderingContext
のDataObject
を取得できます。
LinkBean bean = new LinkBean();
DataBoundValue destinationBinding =
new DataBoundValue(_EXAMPLE_NAMESPACE,
"dataObjName",
"url");
bean.setAttributeValue(UIConstants.DESTINATION_ATTR,
destinationBinding);
// ...
static private final String _EXAMPLE_NAMESPACE =
"http://www.example.org";
これは、レンダリング時に次のJavaコードを実行していることと同じです。
// ...
DataObject dataObject = context.getDataObject("http://www.example.org",
"dataObjName");
Object destination = dataObject.selectValue(context, "url");
// ...
DataBoundValue
が有用なため、DataBoundValue
を作成する次のような便利なメソッドがBeanに用意されています。
LinkBean bean = new LinkBean();
bean.setDestinationBinding(_EXAMPLE_NAMESPACE
"dataObjName",
"url");
DataObject
の登録: DataProvider
ここまでで、独自のDataObject
を記述する方法、それらをRenderingContext
から取得する方法、およびそれらを属性にバインドする方法を学びました。 この他に説明が必要なものは、DataObject
をRenderingContext
に渡す方法です。 この方法では別のインタフェース、DataProvider
インタフェースを使用します。 これは小規模なインタフェースです。
public interface DataProvider
{
public DataObject getDataObject(
RenderingContext context,
String namespace,
String name);
// ... methods omitted
}
まだ説明していないメソッドがいくつかありますが、中でもgetDataObject
は重要なメソッドです。これは、DataObject
を取得するためにRenderingContext
でコールするメソッドです。 このメソッドにはネームスペースと名前が渡されるため、各DataProvider
では様々な多くのDataObject
を提供できます。 最後に、DataProvider
をRenderingContext
に連結する方法です。これは、もう1つのメソッドを使用するだけで可能です。
public interface RenderingContext
{
// ...
public void addDataProvider(DataProvider provider);
// ...
}
DataProvider
をRenderingContext
にする回数で何度でもこのメソッドをコールできます。 (実際には、すべてのDataProvider
をTableDataProvider
の中に入れ、それをCachingDataProvider
の中に入れる方が適切です。) これで準備ができました。 次の要約の後、全工程を示す例に進みます。 図3-1を参照してください。
図3-1: DataObject、DataProvider、RenderingContextおよびUINodeの関係
DataProvider
はDataObject
のコレクションです。DataObject
は任意のデータのコレクションです。RenderingContext
は、DataProvider
からDataObject
を取得します。DataBoundValue
オブジェクト(またはUIX XMLのdataObject
要素)によって、すべてがUINode属性に連結されます。DataProvider
の例: UIX Components非常に単純なページを作成します。 リンクが1つのみあり、コンテンツを1つのDataObject
にデータ・バインドします。 すべてをJSPで表示します。 最初にいくつかの定数を定義します。
public class DataDemo
{
// ...
private static final String _DEMO_NAMESPACE = "http://www.example.org/";
private static final String _DATA_OBJECT_NAME = "TextData";
private static final Object _TEXT_KEY = "textKey";
private static final Object _DESTINATION_KEY = "urlKey";
}
ここで、UIX Componentsのノードを作成する関数を記述します。
public class DataDemo
{
static public UINode getPage()
{
// An ultra-simple example - we'll create a single link:
LinkBean link = new LinkBean();
link.setTextBinding(
new DataBoundValue(_DEMO_NAMESPACE,
_DATA_OBJECT_NAME,
_TEXT_KEY)
);
// Or, we can use the built-in DataBoundValue convenience methods
link.setDestinationBinding(_DEMO_NAMESPACE,
_DATA_OBJECT_NAME,
_DESTINATION_KEY);
// And put the link inside of a "BodyBean" (see below)
BodyBean body = new BodyBean();
body.addIndexedChild(link);
return body;
}
// ...
}
このコードは、前に作成したコードによく似ています。 リンクのBeanを作成し、2つの属性をデータ・オブジェクトにバインドしています。 新しく追加したのはBodyBean
のみです。 これはデータ・バインドの一部ではありません。HTMLの<body>
タグの作成に常に使用されるUIX Components Beanです。
ここで、DataProviderを設定する関数を記述します。
注意: これは非常に基本的な例です。データがハードコードされており、基本のDictionaryData
クラスを使用してそのデータを格納しています。 ただし、レンダリング時にJSPのPageContext
を渡すことができます。つまり、そのコンテキストを使用してここで必要なすべての状態を取得できます。 さらに、DataProvider
およびDataObject
にデータが要求される際、RenderingContext
はその両方に渡されます。 また、ServletRequest
やHttpSession
などの一般的なサーブレット・オブジェクトをRenderingContextから取得できるため、すべてのデータを事前にロードしておく必要はありません。 データが実際に要求されるまで待機できます。
public class DataDemo
{
// ...
static public DataProvider getDataProvider()
{
DataObject data = _getData();
// And put it in a DataProvider
TableDataProvider provider = new TableDataProvider();
provider.put(_DEMO_NAMESPACE, _DATA_OBJECT_NAME, data);
return provider;
}
static private DataObject _getData()
{
// Build up a DataObject
DictionaryData data = new DictionaryData();
data.put(_TEXT_KEY, "Shameless promotion!");
data.put(_DESTINATION_KEY, "http://www.oracle.com");
return data;
}
// ...
}
ここで使用したTableDataProvider
に注意してください。 このクラスでは、DataObject
やその他のDataProvider
を、ネームスペースおよび名前によって分割します。
最後に、レンダリングを行うJSPを記述します。
<%@ page contentType="text/html" %>
<%@ page import='oracle.cabo.ui.ServletRenderingContext'%>
<%@ page import='oracle.cabo.ui.beans.StyleSheetBean'%>
<%@ page import='oracle.cabo.ui.data.DataProvider'%>
<%@ page import='yourpackage.DataDemo'%>
<html>
<head>
<%
// Create a rendering context
ServletRenderingContext rContext =
new ServletRenderingContext(pageContext);
// Include the stylesheet that UIX Components needs
StyleSheetBean.sharedInstance().render(rContext);
%>
</head>
<%
// Get the data provider, and attach it to the
// rendering context
DataProvider provider = DataDemo.getDataProvider();
rContext.addDataProvider(provider);
DataDemo.getPage().render(rContext);
%>
</html>
この例を実行すると、「Shameless promotion!」という語句が表示されます。これは、オラクル社のWebサイトにリンクされています。
この例では、別のBean、StyleSheetBean
も使用しています。 このBeanでは、UIX Componentsのすべてのページで必要とされるスタイルシートを自動的に追加します。 このBeanの使用による、1行のコードでの高性能処理やカスタマイズの実現については、後半のトピックで説明します。 ここでは、このBeanがHTMLのすべての<head>
に属することを理解してください。
必要な作業はこれですべてです。1つの単純なリンクをレンダリングするには少々過度な処理です。 ただし、データベースとの対話、ローカライズされたテキストの提供、またはローカライズされたデータに関するその他のソースの提供を可能にする、汎用のDataProvider
が使用されています。
ここまでで、BoundValue
とDataObject
を使用して、動的データを含むページを設定する方法を学びました。 ただし、DataObject
インタフェースでは、一連のプロパティがサポートされるだけです。 表形式のデータ、またはJava配列のようなその他のデータを表示する場合、これは非常に不便なインタフェースです。 そのような場合に、DataObjectList
インタフェースが使用されます。
DataObjectList
このトピックで説明する最後の新規インタフェースは、単純なインタフェースです。
public interface DataObjectList
{
public int getLength();
public DataObject getItem(int index);
}
DataObjectList
は、実際にはDataObject
配列です。 (実際には配列を使用しないため、DataObjectList
を不変に保つことができます。何かを設定するメソッドはないため、DataObject
を作成するか、DataObject
インスタンスを再利用することになります。)
ArrayDataSet
クラスは、DataObjectList
の単純で便利な実装です。 DictionaryData
のように使用方法が単純ですが、すべてのDataObject
を事前に作成する必要があります。
DataObject[] array = new DataObject[10];
for (int i = 0; i < 10; i++)
{
array[i] = ...;
}
ArrayDataSet list = new ArrayDataSet(array);
DataObjectList
へのバインド: 現在のDataObject
ここで、データをDataObjectList
から取り出して、ページに戻す方法について説明します。 それには、現在のDataObject
を指定することが必要です。
すべてのデータ・フレームワークで、データの反復処理がサポートされています。 これはJavaではfor
ループに相当します。 データベースではカーソルに相当します。 UIX Componentsでは、現在のDataObject
の指定がそれに当たります。
このトピックで前述したRenderingContext.getDataObject
メソッドを思い出してください。 これまで触れていませんが、DataObject
を取得するためのメソッドがもう1つあります。
public interface RenderingContext
{
// ..
/**
* Get a DataObject by namespace and name.
*/
public DataObject getDataObject(String namespaceURI,
String localName)
/**
* Get the "current" DataObject.
*/
public DataObject getCurrentDataObject();
// ..
}
この新しいメソッドgetCurrentDataObject()
は、現在のDataObject
を返します。これは、データベース・カーソルに相当するUIX Componentsのメソッドであり、DataObjectList
へのバインドに使用します。
Javaで現在のDataObject
からデータを取得するには、1つの引数を使用するDataBoundValue
の特別なコンストラクタ、または1つの引数を使用するBeanの便利なメソッドを使用します。
StyledTextBean textBean = new StyledTextBean();
textBean.setAttributeValue(UIConstants.TEXT_ATTR,
new DataBoundValue(TEXT_KEY));
// ... OR, EASIER ...
textBean.setTextBinding(TEXT_KEY);
ここまでの内容をすべて使用して、TableBean
のためのDataObjectList
を作成してみましょう。
public class DataDemo
{
// ...
static private DataObjectList _getDataForTable()
{
DataObject[] rows = new DataObject[5];
for (int i = 0; i < rows.length; i++)
{
DictionaryData row = new DictionaryData();
row.put(_TEXT_KEY, "Row " + i);
row.put(_TEXT_2_KEY, "Column 2, row " + i);
rows[i] = row;
}
return new ArrayDataSet(rows);
}
// ...
private static final Object _TEXT_2_KEY = "text2Key";
}
ここでも、例を単純にするためにハードコードされたデータを使用しましたが、実際のアプリケーションではより多くのデータを処理できます。 ここで、このDataObjectList
を表に入れる必要があります。 次のようにコールできます。
TableBean table = new TableBean();
table.setTableData(_getDataForTable());
ただし、このように設定されたデータは動的にならないため、リクエストごとにTableBean
を再利用することはできません。 データを動的にするために、前のデモで使用したものと同じDataObject
にデータを追加できます。 新しいキーも必要になります。また、コードを変更して、TableBean
をページに追加します。
public class DataDemo
{
// ...
static public UINode getPage()
{
// ... old code ...
// And now, let's create the table
TableBean table = new TableBean();
table.setTableDataBinding(_DEMO_NAMESPACE,
_DATA_OBJECT_NAME,
_TABLE_DATA_KEY);
body.addIndexedChild(table);
return body;
}
static private DataObject _getData()
{
// Build up a DataObject
DictionaryData data = new DictionaryData();
// ... old code ...
// And now, add the DataObjectList
data.put(_TABLE_DATA_KEY, _getDataForTable());
return data;
}
// ...
static private final Object _TABLE_DATA_KEY = "tableKey";
}
前に示した例のように、DataProvider
を使用して、DataObject
がRenderingContext
に追加されます。 このコードを変更する必要はありません。 また、表データのバインドに以前と同じコードを使用していることに注意してください。tableData
は単に別の属性であり、その他の要素は変わっていません。
ここには多数のオブジェクトがあります。 作成したオブジェクトについて見なおします。
DataProvider
は1つのDataObject
を提供します。DataObject
には、2つのテキストと1つのDataObjectList
が含まれます。DataObjectList
には、5つのDataObject
が含まれます。DataObject
には2つのテキストが含まれます。現在のDataObject
の指定が機能している例はまだ示していませんが、表に列を追加した後で、現在のDataObject
の指定を使用する必要が生じます。 TableBean
の各列は、索引付けされた子によって表されます。 addIndexedChild()
を3回コールすると、表に3つの列が作成されます。 これらの各列は、行ごとに子要素をスタンプとして一度使用することによってレンダリングされます。
異なる方法でレンダリングされる各要素を行ごとに取得する点が特長です。 これは、現在のDataObject
の指定を使用することで可能になります。 表で最初の行をレンダリングする際、RenderingContext.getCurrentDataObject()
によって、tableData
のDataObjectList
の最初のDataObject
が返されます。 2行目のレンダリング時には、getCurrentDataObject()
は2番目のDataObject
を返します。 以下同じ処理が繰り返されます。
ここで表に2つの列を追加します。1つは通常のテキスト列、もう1つは赤色の列です。 (この例では、StyleSheetBean
で提供されるスタイルの1つである、ビルトインのOraErrorText
CSSスタイルを使用しています。)
public class DataDemo
{
// ...
static public UINode getPage()
{
// ... old code ...
// And now, let's create the table
TableBean table = new TableBean();
table.setTableDataBinding(...);
// Create the first column
StyledTextBean firstColumn = new StyledTextBean();
// Use _TEXT_KEY for the data of the first column
firstColumn.setTextBinding(_TEXT_KEY);
// Add it to the table
table.addIndexedChild(firstColumn);
// Create the second column, using _TEXT_2_KEY
StyledTextBean secondColumn = new StyledTextBean();
secondColumn.setStyleClass("OraErrorText");
secondColumn.setTextBinding(_TEXT_2_KEY);
table.addIndexedChild(secondColumn);
// ...
}
// ...
}
これで終了です。 ここで、このレンダリング方法について説明します。 表では、外枠や背景の設定など多くの処理が実行されます。 その後で最初のセルがレンダリングされます。
行0、列0については、表は最初のStyledTextBean
を使用します。 RenderingContext.getCurrentDataObject()
によって、DataObjectList
の最初のDataObject
が返されます。これには、「Row 0」および「Column 2, row 0」のエントリがあります。 StyledTextBean
によって、テキストであるDataBoundValue
が要求されます。 現在のDataObject
が取得され、_TEXT_KEY
が要求されます。_TEXT_KEY
は「Row 0」を返し、そのセルにレンダリングされます。
次に、行0、列1が処理されます。同じ、現在指定されているDataObject
が有効ですが、もう1つのStyledTextBean
がレンダリングを行います。 これは_TEXT_2_KEY
を要求し、「Column 2, row 0」をレンダリングします。
次は行1です。最初のStyledTextBean
に戻りますが、ここではすでにDataObjectList
の次のDataObject
に移動しています。 そのため、RenderingContext.getCurrentDataObject()
は異なるDataObject
を返します。 この行の最初の列には「Row 1」、2番目の列には「Column 2, row 1」が入ります。
表全体についてこの処理が繰り返されます。 これは単純な例です。列ヘッダーや行ヘッダー、あるいは表で実行可能な多数の処理は追加していません。また、編集不可の普通のテキストのみを使用しています。 ただし、これらはすべて同じ方法で機能します。 表の行ごとにレンダリング方法を変える場合、現在指定されているDataObject
に、その変更内容を示す情報が指定されている必要があります。
たとえば、各行にリンクを指定し、それぞれの宛先が異なるとします。 この場合は簡単です。 LinkBean
を使用して、destination
属性を現在指定されているDataObject
にデータ・バインドし、各行の宛先を含むようにDataObjectList
を更新します。 UIX Componentsでは、すべてについてデータ・バインドが可能なため、各行が異なるようにできます。
BeanAdapterUtils
多くの開発者にとって、JavaBeans、マップ、リストおよび配列がDataObject
またはDataObjectList
に変換されるメカニズムは依然として理解しにくいでしょう。 しかし、詳細を理解するのは有用だと考える開発者もいるでしょう。 まず、いくつかのAPIは、特にJava Beansで、DataObject
またはDataObjectList
を明示的に受け入れます。 第2に、アダプタをカスタマイズして、機能を追加したり、パフォーマンスを改善したりする場合があります。 どちらの場合も、oracle.cabo.ui.data.bean.BeanAdapterUtils
を使用する必要があります。
任意のオブジェクト(Bean、Map
またはDataObject
そのもの)をDataObject
に変換するには、BeanAdapterUtils.getAdapter()
メソッドの1つを使用します。ほとんどの場合、次のとおりです。
public static DataObject getAdapter(RenderingContext context, java.lang.Object instance)
配列またはList
をDataObjectList
に変換するには、次のメソッドを使用します。
public static DataObjectList getAdapterList(RenderingContext context, java.lang.Object listInstance)
パフォーマンスを改善する前に、問題があることを確認します。 アダプタ・レイヤーは軽量でパフォーマンスが良好なので、ほとんどのアプリケーションではコードの一部の最適化を問題視する必要がありません。
まずは、必要になるたびにアダプタ・オブジェクトを作成することを避けます。 UIXで自動的にアダプタを作成するのではなく、手動で作成してその結果をキャッシュします。 次に例を示します。
public class DataDemo
{
static public Object getLinkBean(
RenderingContext context,
String namespace,
String name)
{
return _sAdapter;
}
static private LinkDataBean _sInstance =
new LinkDataBean("http://www.oracle.com",
"Shameless bean promotion!");
// Create a static adapter:
static private DataObject _sAdapter;
static
{
try
{
_sAdapter = BeanAdapterUtils.getAdapter(_sInstance);
}
catch (InstantiationException ie) { }
catch (IllegalAccessException iae) { }
}
}
これは、Beanの存続期間が長い場合のみ有効です。 Beanが1つのリクエストのみで使用されている場合、アダプタのキャッシュはそれほど有効ではありません。ただしそれがセッション・レベルまたはアプリケーション・レベルのBeanの場合、これはわずかながら簡単なパフォーマンス向上につながります。
次に、独自のDataObject
アダプタを記述して、イントロスペクションの本質的なパフォーマンスの問題に取り組みます。 または、より効果的な方法として、アダプタを自動的に記述できます。 UIXには、コンパイル済JavaBeansを取得しDataObject
アダプタを自動的に記述する、BuildBeanDOAdapter
というJavaベースのツールが組み込まれています。 コマンドラインで次のように入力します。
java oracle.cabo.ui.tools.BuildBeanDOAdapter yourpackage.LinkDataBean
ここで生成されるコードすべては列記しませんが、次に注目すべき部分を示します。selectValue()
コードが例のLinkDataBean
に対して生成されています。
public Object selectValue(RenderingContext context, Object select)
{
LinkDataBean instance = _instance;
if (instance == null)
return null;
try
{
if ("url".equals(select))
return instance.getUrl();
if ("text".equals(select))
return instance.getText();
}
catch (Exception e)
{
context.getErrorLog().logError(e);
}
return null;
}
これはイントロスペクションよりもはるかに速い動作です。 最新のベンチマーク(Windows 2000上のJava 1.3.0)では、20から30倍速いとされています。 ただし、数百ものプロパティがある大規模なJavaBeansでは、String.equals()
に対するこれらのコールは重大な問題となり、この最適化が実際にはパフォーマンスにとって有害となる可能性があります。 これらのケースのため、アダプタ構築ツールは-fast
コマンドライン・オプションをサポートしています。 結果として生成されたクラスはStringハッシュコードを効果的に使用して、selectValue()
のスピードを基本イントロスペクションよりも速いレベルまで再び引き上げます。 どちらの方法でアダプタ・クラスを構築しても、これらのアダプタは、boolean
およびint
の戻り値をBoolean
およびInteger
のオブジェクトに変換するような一般的なイントロスペクションよりもはるかにスムーズでもあります。
これらのアダプタ・クラスを一度作成すると、データ・バインド・コードを変更してこれらのアダプタを明示的に作成できます。
public class DataDemo
{
static public Object getLinkBean(
RenderingContext context,
String namespace,
String name)
{
LinkDataBean bean = ...;
return new LinkDataBeanDataObject(bean);
}
}
ただし、ここまでのすべての手順を確実にこなしていくことを確認するのは冗長な(エラーも発生しやすい)作業です。 代替案としてアダプタを、静的なregisterAdapter()
コールによってBeanAdapterUtils
に直接登録できます。 このコールを初期化コードに追加すると、残りのコードは以前のようにBeanインスタンスを続けて返すことができます。
public class DataDemo
{
static public Object getLinkBean(
RenderingContext context,
String namespace,
String name)
{
// Under the covers, UIX will use LinkDataBeanDataObject to
// adapt this bean, but you don't need to know that here
return new LinkDataBean(...);
}
// Register the adapter once
static
{
LinkDataBeanDataObject.registerAdapter();
}
}
UIXでは、JavaBeans仕様のすべての側面にデータが完全に依存している必要はありませんが、完全な方がより好ましい状態です。 次のような状況はすべて自動的に処理されます。
クラスが、引数を持たないコンストラクタや、publicコンストラクタさえまったく持つ必要がない。
クラスが、シリアライズ可能である必要がない。
クラスが、set
メソッドを持つ必要がない(少なくともデータ・バインドに関してはget
メソッドのみ留意される)。
LinkDataBean
を次のように記述することも可能です。
public class LinkDataBean
{
public LinkDataBean(String url, String text)
{
this.url = url;
this.text = text;
}
public String url;
public String text;
}
これは通常、独自のJavaBeansではよい設計と言えませんが、特別なアダプタがなくてもjava.awt.Point
のようなレガシー・クラスをサポートできることを意味しています。
JavaBeansではメソッドのみでなく、クラス自体がpublicになることが必要です。 よい設計の原則でこれらのクラスをpublicにしないように推奨される場合があるため、この制限事項には少々問題があります。 UIXの内部変換で次の代替方法をサポートします。
publicインタフェースでメソッドを記述します。 次に例を示します。
// The interface is public...
public interface LinkData
{
public String getText();
public String getUrl();
}
// ...but the bean is private
class LinkDataBean implements LinkData
{
...
}
publicスーパークラス(通常は抽象だがそうではない場合もあり)でメソッドを記述します。
data:
の変換: 式構文からEL構文UIXリリース2.2以上(Oracle9i JDeveloper 9.0.5以上に付属)では、EL(式言語)データ・バインドがサポートされます。 これは、現在ではUIXでのデータ・バインドの推奨方法です。 このセクションでは、UIXの以前のバージョンで使用されているdata:
構文をEL構文に変換する方法を説明します。
注意: UIXでは古いdata:
構文もサポートされていますが、ELの方がより強力です。また、ELは、Oracle Application Development Framework(ADF)およびOracle9i JDeveloperでの標準のデータ・バインド・メカニズムです。 このトピックの最初に示したとおり、このトピックの内容は基本的には以前のバージョンと同じですが、データ・バインド構文がELに置き換えられています。
既存のUIX XMLページの古い構文を新しい構文に変換する基本的な手順を次に示します。
EL構文を使用することを指定します。 これは、アプリケーション・レベルまたはページごとにデフォルトとして設定できます。
ELをアプリケーションのデフォルトに設定するには、uix-config.xml
構成ファイル内の<application-configuration>
の子として<default-expression-language>el</default-expression-language>
を追加します。 「ADF UIXの構成」のトピックの「<application-configuration>の編集」を参照してください。
ページ・レベルでは、属性expressionLanguage="el"
を、ルート要素<ctrl:page ...>
、<ui:page ...>
または<ui:templateDefinition ...>
に設定します。 他のルート要素は、<ui:page expressionLanguage="el">
要素内にラップします。 詳細は、「UIX XMLページで使用するデータ・バインド構文の設定」を参照してください。
UIX XMLページの<page>
要素から、data
ネームスペースへの参照を削除します。 すなわち、xmlns:data="http://xmlns.oracle.com/uix/ui
を削除します。
data:
構文を新しいEL構文に変換します。 正式な構文は、UIX 2.2のリリース・ノートを参照してください。 次のセクションの例も参照してください。このセクションでは、変換の正式な構文を説明するかわりに(この説明は複雑になるため)、古い構文を新しい構文に変換する例を多数示します。 既存のUIX XMLコードに類似した例を検索し、 その例に示されている新しい構文を参照して変換できます。 これらの例は、UIXバンドルに含まれているショッピング・カート・デモからの抜粋です。
<!-- Old syntax: -->
<header data:text="ToDoList@text">
<!-- New syntax: -->
<header text="${text.ToDoList}">
<!-- Old syntax: -->
<contentContainer width="30%" data:text="NewToPurchasing">
<!-- New syntax: -->
<contentContainer width="30%" text="${uix.current.NewToPurchasing}">
<!-- Old syntax: -->
<link data:text="BrowseCategories@text"
ctrl:destination="BrowseCats"
ctrl:event="BrowseCatalog" />
<!-- New syntax: -->
<link text="${text.BrowseCategories}"
destination="${ctrl:destination(uix, 'BrowseCats','BrowseCatalog')}" />
<!-- Old syntax: -->
<link data:text="More@text"
ctrl:destination="OrderHistory" />
<!-- New syntax: -->
<link text="${text.More}"
destination="${ctrl:pageUrl(uix,'OrderHistory')}"/>
<!-- Old syntax: -->
<link text="text"
ctrl:event="Order" />
<!-- New syntax: -->
<link text="text"
destination="${ctrl:eventUrl(uix,'Order')}" />
<!-- Old syntax: -->
<frame ctrl:source="panel" />
<!-- New syntax: -->
<frame source="${ctrl:pageUrl(uix,'panel')}" />
<!-- Old syntax: -->
<contents data:childData="link@builtInLinks">
<link data:text="linkText">
<boundAttribute name="destination">
<ctrl:pageURL event="BrowseCatalog"
name="BrowseCats">
<ctrl:parameters>
<ctrl:parameter key="ID"
data:value="linkID"/>
</ctrl:parameters>
</ctrl:pageURL>
</boundAttribute>
</link>
</contents>
<!-- New syntax: -->
<contents childData="${builtInLinks.link}">
<link text="${uix.current.linkText}">
<boundAttribute name="destination">
<ctrl:pageURL event="BrowseCatalog"
name="BrowseCats">
<ctrl:parameters>
<ctrl:parameter key="ID"
value="${uix.current.linkID}"/>
</ctrl:parameters>
</ctrl:pageURL>
</boundAttribute>
</link>
</contents>
<!-- Old syntax: -->
<boundAttribute name="text">
<messageFormat data:format="WelcomeMask@text">
<dataObject select="username" source="loginData"/>
</messageFormat>
</boundAttribute>
<!-- New syntax: -->
<boundAttribute name="text">
<messageFormat format="${text.WelcomeMask}">
<dataObject select="username" source="loginData"/>
</messageFormat>
</boundAttribute>
<!-- Old syntax: -->
<tableLayout>
<contents>
<messageStyledText data:prompt="NumberOfHits@text"
data:text="hits@counter"/>
<messageStyledText data:prompt="UserSessions@text"
data:text="sessions@counter"/>
<messageStyledText data:prompt="Since@text" >
<boundAttribute name="text">
<dateFormat timeStyle="short"
dateStyle="full">
<dataObject select="date" source="counter"/>
</dateFormat>
</boundAttribute>
</messageStyledText>
</contents>
</tableLayout>
<!-- New syntax: -->
<tableLayout>
<contents>
<messageStyledText prompt="${text.NumberOfHits}"
text="${counter.hits}"/>
<messageStyledText prompt="${text.UserSessions}"
text="${counter.sessions}"/>
<messageStyledText prompt="${text.Since}" >
<boundAttribute name="text">
<dateFormat timeStyle="short"
dateStyle="full">
<dataObject select="date" source="counter"/>
</dateFormat>
</boundAttribute>
</messageStyledText>
</contents>
</tableLayout>
<!-- Old syntax: -->
<table name="OrderHistory" width="60%">
<columnHeaderData>
<t data:value="Order@text" />
<t data:value="Created@text" />
<t data:value="Status@text" />
</columnHeaderData>
<tableData>
<!-- In a real application, the values for "created" would -->
<!-- be generated on the server and properly formatted per -->
<!-- locale. Literal values are given to simplify the example. -->
<rows order="1234" created="May 5 2000"
data:status="Submitted@text" />
<rows order="2312" created="April 13 2000"
data:status="Approved@text" />
<rows order="2311" created="February 27 2000"
data:status="Approved@text" />
</tableData>
<columnHeaderStamp>
<text data:text="value"/>
</columnHeaderStamp>
<contents>
<styledText data:text="order" />
<styledText data:text="created" />
<styledText data:text="status" />
</contents>
<table>
<!-- New syntax: -->
<table name="OrderHistory" width="60%">
<columnHeaderData>
<t value="${text.Order}"/>
<t value="${text.Created}"/>
<t value="${text.Status}"/>
</columnHeaderData>
<tableData>
<!-- In a real application, the values for "created" would -->
<!-- be generated on the server and properly formatted per -->
<!-- locale. Literal values are given to simplify the example. -->
<rows order="1234" created="May 5 2000"
status="${text.Submitted}" />
<rows order="2312" created="April 13 2000"
status="${text.Approved}" />
<rows order="2311" created="February 27 2000"
status="${text.Approved}" />
</tableData>
<columnHeaderStamp>
<text text="${uix.current.value}"/>
</columnHeaderStamp>
<contents>
<styledText text="${uix.current.order}" />
<styledText text="${uix.current.created}" />
<styledText text="${uix.current.status}" />
</contents>
</table>
<!-- Old syntax: -->
<boundAttribute name="text">
<messageFormat data:format="WelcomeMask@text">
<dataObject select="username" source="loginData"/>
</messageFormat>
</boundAttribute>
<!-- New syntax: -->
<boundAttribute name="text">
<messageFormat format="${text.WelcomeMask}">
<dataObject select="username" source="loginData"/>
</messageFormat>
</boundAttribute>
<!-- Old syntax: -->
<bulletedList>
<contents>
<link data:text="Shop@commonText"
ctrl:destination="BrowseCats"/>
<link data:text="OrderHistory@commonText"
ctrl:destination="OrderHistory"/>
<link data:text="ShoppingCart@commonText"
ctrl:destination="ShopCart"/>
<link data:text="Preferences@commonText"
ctrl:destination="Preferences"/>
</contents>
</bulletedList>
<!-- New syntax: -->
<bulletedList>
<contents>
<link text="${commonText.Shop}"
destination="${ctrl:pageUrl(uix, 'BrowseCats')}"/>
<link text="${commonText.OrderHistory}"
destination="${ctrl:pageUrl(uix, 'OrderHistory')}"/>
<link text="${commonText.ShoppingCart}"
destination="${ctrl:pageUrl(uix, 'ShopCart')}"/>
<link text="${commonText.Preferences}"
destination="${ctrl:pageUrl(uix, 'Preferences')}"/>
</contents>
</bulletedList>
<!-- Old syntax: -->
<text data:text="street@address@userData"/>
<!-- New syntax: -->
<text text="${userData.address.street}"/>
<!-- Old syntax: -->
<text data:text="name@(selectedItem)@userData"/>
<!-- New syntax: -->
<text text="${userData[uix.current.selectedItem].name}"/>
<!-- Old syntax: -->
<text data:text="name@(person@selectedItem)@userData"/>
<!-- New syntax: -->
<text text="${userData[selectedItem.person].name}"/>
<!-- Old syntax: -->
<text data:text="name@prefix:userData"/>
<!-- New syntax: -->
<text text="${prefix:data().userData.name}"/>
<!-- Old syntax: -->
<submitButton text="Foo" ctrl:event="Bar"/>
<!-- New syntax: -->
<submitButton text="Foo" event="Bar"/>
<!-- Old syntax: -->
<formValue ctrl:event="Bar"/>
<!-- New syntax: -->
<formValue name="${ui:encodeParameter(uix,'event')}" value="Bar"/>
<!-- Old syntax: -->
<include ctrl:node="Home"/>
<!-- New syntax: -->
<include node="${ctrl:parsePage(uix,'Home')}"/>
<!-- Old syntax: -->
<text text="Untitled" data:text="text@userdata"/>
<!-- New syntax: -->
<text text="${ui:defaulting(userdata.text,'Untitled')}"/>
Copyright © 2001, 2004, Oracle Corporation.
All rights reserved.