UIX開発者ガイド
Go to Table of Contents
目次
Go to previous page
前へ
Go to next page
次へ

4. データ・バインド

このトピックでは、ノード・ツリー自体は変更せずに、uiXMLのデータ・バインド属性を使用して、ユーザーごとに動的コンテンツを生成するページの作成方法を説明します。UIXによるJavaBeansおよびデータをグループ化するためのMapのサポート方法や、配列およびListを持つデータの反復方法を説明します。また、インライン・データをプロトタイプ用のuiXMLに直接作成する方法も説明します。次に、Java開発者はこの機能をサポートするDataObjectBoundValueDataProviderおよびDataObjectListインタフェースや、これらのJava APIを使用してさらにカスタマイズされたデータ・バインドを実装する方法を学習します。 最後に、JavaBeansに対するUIXサポートの詳細を説明します。

ここでは、次の項目について説明します。

uiXMLからのデータ・バインド

以降いくつかのセクションでは、uiXML開発者の観点からUIXのデータ・バインドについて説明します。Java APIを使用してUIXの開発のみを行っている場合は、「Java APIからのデータ・バインド」へスキップしてもかまいません。

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つの作業が必要です。

  1. UIXで、text属性をデータ・バインドします。
  2. <dataScope>をページに追加し、コンテンツにデータを供給します。
  3. Beanにアクセス可能な小規模のデータ・プロバイダをJavaで作成します。

まず、textをデータ・バインドします。

<text xmlns="http://xmlns.oracle.com/uix/ui"
      xmlns:data="http://xmlns.oracle.com/uix/ui"
      data:text="time@currentDate"/>

最初の例から次の3箇所が変更されました。

このサンプル・コードを実行しても、まったく何も起こりません。currentDateをページに設定していないため、データ・バインドに失敗し、textがNULLのままだからです。この設定をするには、<dataScope>をページに追加します。

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
                    xmlns:data="http://xmlns.oracle.com/uix/ui">
  <provider>
    <data name="currentDate">
      <method class="yourpackage.DataDemo" method="getCurrentDate"/>
    </data>
  </provider>
  <contents>
    <text data:text="time@currentDate"/>
  </contents>
</dataScope>

これで、大幅な変更が加えられました。結果は次のとおりです。

このページに入力してこれを実行した場合、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();
    }
  }

これは、単純なクラスです。uiXMLの<method>要素によって参照されると記述された、すべてのJavaのデータ・プロバイダには、ここで確認できるものと同一のシグネチャが設定されている必要があります。このサンプル・コードにある3つのパラメータはいずれも必要ではありませんが、このJavaBeansを返す必要があります。このメソッドをCurrentDateBeanクラスに移動することも可能です。しかしこのコーディング・スタイルはお薦めしません。JavaBeansはどのアーキテクチャでも再利用可能なように設計する必要があります。さらに、UIX、サーブレットまたは他のテクノロジへの依存をできるだけ排除する必要があります。

先へ進む前に、2つの疑問点を解決しましょう。

  1. DataDemo.getCurrentDate()がコールされるタイミング

    DataDemo.getCurrentDate()は、ページがレンダリングされるたびに1回コールされます。CurrentDateBeanに複数のプロパティが設定されていて、そのすべてを使用した場合でも、Beanは1回ロードされます。しかし、getCurrentDate()はページがレンダリングされるたびにコールされます。ページを100回表示する必要がある場合、このメソッドは100回コールされます。

  2. CurrentDateBean.getTime()がコールされるタイミング

    CurrentDateBean.getTime()は最低1回コールされます。UIXでは、これにより各プロパティが1回しか要求されないとは限りません。繰り返し要求される場合もあります。通常getTimeによって新しいDateオブジェクトが作成されるので、ページでtimeを何度も使用すると、異なる時刻がページに表示される可能性があります。これが問題であるならば、DateCurrentDateBeanコンストラクタに作成し、getTime()がコールされるたびに同じオブジェクトを再利用してください。

データ・バインドでのマップの使用

JavaBeansは、データをカプセル化する適切な方法です。しかしUIXで状態を設定するたびに新しいJavaBeansクラスを記述したり、新しいプロパティが使用されるたびに新しいgetterメソッドを追加したりするのは、単調で時間を要します。また、すべてのプロパティ名を事前に把握するのは不可能な場合もあります。その場合は実行時に検出するしかありません。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"
                    xmlns:data="http://xmlns.oracle.com/uix/ui">
  <provider>
    <data name="link">
      <method class="yourpackage.DataDemo" method="getURLAndText"/>
    </data>
  </provider>
  <contents>
    <text data:text="text@link"
           data:destination="url@link"/>
  </contents>
</dataScope>

この方法で開発するのは簡単ですが、JavaBeansを使用する場合ほど適切ではありません。いつどの方法を使用するかは個人の好みの問題であり、ほとんどの開発者は両方の手法を使用することになるでしょう。

<boundAttribute>要素

最初の例を少し拡張してみましょう。単に時刻を表示するかわりに、時刻の前に「The time is:」と表示します。この静的テキストが含まれた<text>要素を追加する方法もありますが、ここでは異なる手法を示します。data:を使用する場合、JavaBeansから値を取得できますが、操作はできません。<boundAttribute>要素により、高度な設計ができます。簡単な連結法則を使用した、非常に複雑な式が可能です。

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
                    xmlns:data="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>要素のみです。data:textを使用してtextをデータ・バインドするかわりに、<boundAttribute>を使用しています。これには<concat>要素が含まれており、これによりBeanから(<dataObject>要素とともに取得された)timeが静的文字列に結合されます。

<boundAttribute>内で使用される要素は数十個ありますが、ここではすべてを説明しません。これらの要素の詳細は、JDeveloperのuiXMLエディタにある「コード・インサイト」を使用するか、「UIX要素リファレンス」を参照してください。これらの要素は、対応するJavaインタフェースに由来してBoundValue要素と呼ばれます。他のいくつかのBoundValue要素には、次のものが含まれます。

これらの大半については後半のトピックで説明しますが、全リストは「要素リファレンス」を参照してください。

データ反復処理

ここまでで、動的データを含むページを設定する方法を学びました。しかし、このインタフェースでは、順序付けられていない一連のプロパティがサポートされるだけです。表形式のデータ、またはJava配列のようなその他のデータを表示する場合、これは非常に不便なインタフェースです。UIXでは、ListおよびJava配列の反復をサポートしています。また、UIX固有のAPIであるDataObjectListもサポートしています。これについては後述します。

ここで、Java配列およびListからデータを取り出して、ページに戻す方法について説明します。それには、現在のDataObjectをコールする設定が必要です。すべてのデータ・フレームワークで、データの反復処理がサポートされています。これはJavaではforループに相当します。データベースではカーソルに相当します。UIX Componentsでは、現在のDataObjectの指定がそれに当たります。現在のDataObjectからデータを取得するために、ソース全体を省略します。

<styledText>
 <boundAttribute name="text">
   <dataObject select="textKey"/>
 </boundAttribute>
</styledText>

<!-- OR -->

<!-- Note that there's no "@" sign here -->
<styledText data:text="textKey"/>

<dataScope>のcurrentData属性を使用して、現在のDataObjectを明示的に設定できます。

<dataScope data:currentData="currentDate">
 <!-- Now we have a current data object -->
 ...
</dataScope>

しかし、これは通常使用される方法ではありません。さらに一般的な方法として、Listまたは配列を自動的に反復し、DataObjectを自動設定するコンポーネントを使用します。UIXの<table>は、このようなコンポーネントの1つです。ここでは<table>の詳細は省略します。<table>は、1つのトピックを設ける必要があるほど多機能だからです。ここでの説明は、現在のDataObjectの機能を理解するためのみにとどめておきます。

最初に、表に対してListを作成します。ここで、MapArrayListを使用します。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)
  {
    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 CSSスタイルを使用しています。)


<dataScope>
  <provider>
    <data name="someTableData">
      <method class="yourpackage.DataDemo" method="getTableData"/>
    </data>
  </provider>
  <contents>
    <table data:tableData=".@someTableData">
      <contents>
        <styledText data:text="text"/>
        <styledText styleClass="OraErrorText" data:text="text2"/>
      </contents>
    </table>
  </contents>
</dataScope>

これで終了です。コードはそれほど長くありませんが、利点は多いです。

まず、<table>要素は、データ・バインドされたtableData属性を使用してデータを取得しています。ここで注目すべき点は、データ・バインドの式にあるピリオド(.)です。先に記述したgetTableData()では、ArrayListが返されました。表のデータには、ArrayListのプロパティではなく、ArrayListそのものが必要です。この構文によって、それが可能です。

次に、2つの子が追加されました。1つ目は、textキーからテキストを取得する<styledText>です。ここにアットマーク(@)がないことに注意してください。つまり、テキストは現在のDataObjectから取得されています。2つ目は、別のキーにバインドされたテキストが設定された、もう1つの<styledText>です。データ・モデルをまったく使用せずに、列の追加、削除およびレンダリングをUIで直接行えます。

これは単純な例です。列ヘッダーや行ヘッダー、あるいは表で実行可能な多数の処理は追加していません。また、編集不可の普通のテキストのみを使用しています。ただし、これらはすべて同じ方法で機能します。表の行ごとにレンダリング方法を変える場合、現在指定されているDataObjectに、その変更内容を示す情報が指定されている必要があります。

たとえば、各行にリンクを指定し、それぞれの宛先が異なるとします。この場合は簡単です。<link>要素を使用してdestination属性を現在指定されているDataObjectにデータ・バインドし、各行の宛先を含むようにDataObjectを更新します。UIXでは、すべてについてデータ・バインドが可能なため、各行が異なるようにできます。

uiXMLでの複雑なデータ・バインド

このセクションでは、キーとデータ・オブジェクトをネストして、さらに複雑なデータ・バインドを生成する方法を説明します。最初に、次のフォームを使用して、ネストされたキー・データ・バインドを生成します。

data:attributeName="keyN@...@key2@key1@name"

この場合、この属性の値を計算する際に、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"
        data:text="city@address@Smith@families" /> <!--San Francisco-->
   <messageTextInput prompt="The Jones's live in"
        data:text="city@address@Jones@families" /> <!--Redwood Shores-->
 </contents>
</dataScope>

上の例では、最初のmessageTextInputで「The Smiths live in San Francisco」が生成され、2番目で「The Jones's live in Redwood Shores」が生成されます。

ネストされたデータ・オブジェクト・バインドを生成するためにカッコも使用できます。次の例について考えてみます。

data:attributeName="(key1@name1)@name2"

上の例では、name1(で参照されるDataObject)でkey1が使用されてキーを生成し、そのキーがname2で使用されてこの属性の最終値を生成しています。これは、キー自体がデータ・バインドされる例です。データ・オブジェクトの一部をデータ・バインドすることもできます。次の2つの例は同じ意味です。

<!-- These two are equivalent -->
data:attributeName="key2@(key1@name)"
data:attributeName="key2@key1@name"

次に示すのは、前の例で作成された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 data:text="current@selection"/> family
    has<link data:text="members@(current@selection)@families" />
    members.
 </contents>
</dataScope>

カッコは、次の例のようにネストすることもできます。

data:attributeName="((key1@name1)@name2)@name3"

この例では、key1name1で使用されてキーを生成し、そのキーがname2で使用され、最終値を計算するname3に対する別のキーを生成します。

アットマーク(@)の前後のテキストはオプションです。キーを省略すると、次の例のようにDataObject自体が返されます。

<!-- The following attribute is bound to the data object itself -->
data:attributeName="@name"

データ・オブジェクト名の一部を省略すると、次の例のように、現在のデータ・オブジェクトでキーが使用されます(アットマーク(@)が指定されない場合のデフォルト)。

<!-- These keys are used with the current data object  -->
data:attributeName="key@"
data:attributeName="key"

アットマーク(@)が指定されない場合、暗黙的にアットマーク(@)があるものと仮定されます(上の例の2番目のバインドの場合)。次の例で、この詳細を示します。

<!-- These are different bindings  -->
data:attributeName="key@name"
data:attributeName="(key@name)"

上の例の2つのバインドは同じではありません。2番目のバインドには暗黙のアットマーク(@)があり、data:attributeName="(key@name)@"と記述することもできます。2番目のバインドでは、keyをデータ・オブジェクトnameに適用し返される値が、現在のデータ・オブジェクトのキーとして使用されます。

キーおよびデータ・オブジェクト名には一定の制限があります。たとえば、キーのテキストにはアットマーク(@)を使用できません。uiXMLの将来のバージョンでは、このような文字のエスケープが可能になり、使用できるようになる予定です。

DataObject API

DataObject Javaインタフェースは、UIX ComponentsおよびuiXMLのページの汎用データソースです。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には、BC4Jへのビルトイン・バインド(「Business Components for Javaの統合」を参照)と、前述のJavaBeansおよびマップへのビルトイン・バインドが組み込まれています。

ほとんどの開発者は、最初にselectパラメータを目にした際にHashtableのキーを想定するでしょうが、DataObjectはそのようには機能しません。selectパラメータは次のいずれかになります。

UIXによる開発では多くの場合、DataObject APIに対してコード化する必要はありません。しかし、マップやJavaBeansのかわりにDataObjectを使用することを考慮してもいい理由があります。 DataObjectはUIXでデータ用に使用される実際の概念です。したがって、Java APIを使用してBeansを作成する場合、メソッドの取得対象であるDataObjectを使用する必要があります。(後述するとおり、BeanAdapterUtilsを使用してJavaBeansをDataObjectに簡単に変換することもできます。)

また、これらのJavaBeansに対するビルトイン・サポートによるオーバーヘッドがまったくないわけではありません。パフォーマンス上のコストがかかります。最初に、DataObjectを実装するアダプタ・オブジェクトを作成する必要があります。これらは軽量であるとは言え、オブジェクト作成は可能ならば避けた方が無難です。次に、イントロスペクション(実行時におけるメソッドの動的な検出およびコール)は本質的に動作が遅くなります。このプロセスの最もコストが高い部分(Methodオブジェクトの検出)のキャッシュについては快適な動作が提供されますが、Methodオブジェクトへのコールについては依然として、手動コーディングされたDataObjectにおけるコンパイル済メソッド・コールよりも動作が遅くなります。

ただし、「早まった最適化は諸悪の根源である(Premature optimization is the root of all evil)」というプログラミングについての有名な格言を思い出してください。手動コーディングされたDataObjectがより速いとしても、DataObjectの記述に時間を費やすべきということにはなりません。適度に使用されていれば、JavaBeansの使用がパフォーマンスに重大な影響を与える理由はありません。本質的に動作が遅いJavaBeans(データベースにアクセスする必要がある場合など)については、アダプタの追加オーバーヘッドは少なくほとんど問題になりません。ただしプロファイリングを実行して、Beansの1つの適合オーバーヘッドがパフォーマンスに重大な影響を与えていると判断した場合は、オブジェクト作成およびイントロスペクション・オーバーヘッドの両方の問題に取り組むことができます。このパフォーマンスに関する問題を解決するいくつかの簡単な方法は、後で説明します。

最後に、そして最も有用な点ですが、DataObjectRenderingContextを受け取ります。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に対して自動的に正しく書式化されます。

uiXMLでのデータおよびデータ・リストの作成

uiXML内にデータとデータ・リストの両方を直接定義できます。ページが実際のデータソースに連結される前にダミー・データを作成できるため、非常に便利です。

まず、DataProviderの新しい要素、<inline>を紹介します。

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
           xmlns:demo="http://www.example.org/">
 <provider>
   <data name="demo:linkData">
     <inline>
        .... data object is in here ...
     </inline>
   </data>
 </provider>

 <contents>
    ...
 </contents>
</dataScope>
この要素は、前述した<method>要素に似た動作をしますが、Javaコードはコールしません。uiXML内でデータを直接定義します。

<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つの内容を含むデータがあります。 非常に簡単です。ここで表の例に戻ります。今回は、インライン・データのみを使用します。
<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 data:tableData="tableKey@inlineData">
     <contents>
       <styledText data:text="text"/>
       <styledText styleClass="OraErrorText" data:text="text2"/>
     </contents>
   </table>
 </contents>

</dataScope>

Java APIからのデータ・バインド

次のいくつかのセクションで、Java API固有の多くの詳細を説明します。全体をuiXMLから開発している場合、独自に作成したuiXMLの機能およびフレームワークの拡張方法を取得するとき、これは重要になる可能性があります。

動的属性: 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のResourceBundleDataObjectに変換します。このクラスは大変便利なため、UIX Componentsに含まれています。ここではその説明を通して、DataObjectを実装する際の基本的な考え方を示します。この例では、DataObjectで例外を処理する方法の1つも示されます(他の方法は「エラーの処理」のトピックを参照してください)。

  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;
  }

留意点: 

  1. ResourceBundleをラップした場合、1つのオブジェクトしか割り当てられません。しかし、Mapを作成してすべてのデータをResourceBundleから取り出した場合、さらに多数のオブジェクトを作成する必要があります。また、前述の例の場合は、実行時に実際に使用されているキーのみが取得されます。
  2. DataObjectで、例外がスローされないようにする必要があります。ここでは、MissingResourceExceptionを検出し、標準のUIX ErrorLogに記録してから、安全な値すなわちnullが返されています。

DataObjectの独自の実装は、非常に簡単に記述できます。カスタムの実装を使用することによって、アプリケーションの効率やパフォーマンスが向上します。

DataObjectRenderingContext

DataObjectの作成後、RenderingContextインタフェースに連結して、UIX Componentsのページで使用できるようにします。(前述のように、このインタフェースが、UIX Components BeanとuiXMLのページに、レンダリングに必要なコンテキストを渡します)。ここで、RenderingContextの特定のメソッドに注目します。

  public DataObject getDataObject(String namespaceURI,
                                  String localName)

このメソッドによって、UIX Components BeanまたはBoundValueでは、任意の数のDataObjectへのアクセスが可能になります。UIXでは通常、これらのDataObjectは2つの文字列で識別されます。WebサイトのURLに基づく1つ以上のネームスペースと、他のコードと競合しない任意のローカル名を使用します。

DataObjectへのバインド: DataBoundValue

ここで、DataObjectRenderingContextに渡す方法からは一度離れ、DataObjectからデータを取得してUINodeまたはuiXMLに渡す方法を説明します。ここで、DataBoundValueクラスが渡されます。UIX Componentsでは、DataBoundValueクラスを使用して、RenderingContextDataObjectを取得できます。

  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から取得する方法、およびそれらを属性にバインドする方法を学びました。この他に説明が必要なものは、DataObjectRenderingContextに渡す方法です。この方法では別のインタフェース、DataProviderインタフェースを使用します。これは小規模なインタフェースです。

  public interface DataProvider
  {
    public DataObject getDataObject(
      RenderingContext context,
      String           namespace,
      String           name);

    // ... methods omitted
  }

まだ説明していないメソッドがいくつかありますが、中でもgetDataObjectは重要なメソッドです。これはDataObjectを取得するためにRenderingContextでコールするメソッドです。このメソッドにはネームスペースと名前が渡されるため、各DataProviderでは様々な多くのDataObjectを提供できます。最後に、DataProviderRenderingContextに連結する方法です。これは、もう1つのメソッドを使用するだけで可能です。

  public interface RenderingContext
  {
    // ...

    public void addDataProvider(DataProvider provider);

    // ...
  }

DataProviderRenderingContextにする回数で何度でもこのメソッドをコールできます。(実際には、すべてのDataProviderTableDataProviderの中に入れ、それをCachingDataProviderの中に入れる方が適切です。)これで準備ができました。次の要約の後、全工程を示す例に進みます。図4-1を参照してください。

図4-1: DataObject、DataProvider、RenderingContextおよびUINodeの関係

Logical representation of relationships among DataObjects, DataProviders, RenderingContexts, and UINodes

  1. DataProviderDataObjectのコレクションです。
  2. DataObjectは任意のデータのコレクションです。
  3. RenderingContextは、DataProviderからDataObjectを取得します。
  4. DataBoundValueオブジェクト(またはuiXMLの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はその両方に渡されます。また、ServletRequestHttpSessionなどの一般的なサーブレット・オブジェクトを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が使用されています。

データ反復処理

ここまでで、BoundValueDataObjectを使用して、動的データを含むページを設定する方法を学びました。ただし、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の独自の実装を記述する必要があることを覚えておいてください。

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を使用して、DataObjectRenderingContextに追加されます。このコードを変更する必要はありません。また、表データのバインドに以前と同じコードを使用していることに注意してください。tableDataは単に別の属性であり、その他の要素は変わっていません。

ここには多数のオブジェクトがあります。作成したオブジェクトについて見なおします。

  1. DataProviderは1つのDataObjectを提供します。
  2. このDataObjectには、2つのテキストと1つのDataObjectListが含まれます。
  3. DataObjectListには、5つのDataObjectが含まれます。
  4. それぞれのDataObjectには2つのテキストが含まれます。

現在のDataObjectの指定が機能している例はまだ示していませんが、表に列を追加した後で、現在のDataObjectの指定を使用する必要が生じます。TableBeanの各列は、索引付けされた子によって表されます。addIndexedChild()を3回コールすると、表に3つの列が作成されます。これらの各列は、行ごとに子要素をスタンプとして一度使用することによってレンダリングされます。

異なる方法でレンダリングされる各要素を行ごとに取得する点が特長です。これは、現在のDataObjectの指定を使用することで可能になります。表で最初の行をレンダリングする際、RenderingContext.getCurrentDataObject()により、tableDataのDataObjectListの最初のDataObjectが返されます。2行目のレンダリング時には、getCurrentDataObject()gは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では、すべてについてデータ・バインドが可能なため、各行が異なるようにできます。

JavaBeans、マップおよびリストの適用: 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)

配列またはListDataObjectListに変換するには、次のメソッドを使用します。

  public static DataObjectList getAdapterList(RenderingContext context,
                                              java.lang.Object listInstance)

JavaBeanアダプタのパフォーマンスの改善

パフォーマンスを改善する前に、問題があることを確認します。アダプタ・レイヤーは軽量でパフォーマンスが良好なので、ほとんどのアプリケーションではコードの一部の最適化を問題視する必要がありません。

まずは、必要になるたびにアダプタ・オブジェクトを作成することを避けます。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 we don't to know that here!
    return new LinkDataBean(...);
  }

  // Register the adapter once
  static
  {
    LinkDataBeanDataObject.registerAdapter();
  }
}

疑似JavaBeansのサポート

UIXでは、JavaBeans仕様のすべての側面にデータが完全に依存している必要はありませんが、完全な方がより好ましい状態です。次のような状況はすべて自動的に処理されます。

  1. クラスが、引数を持たないコンストラクタや、publicコンストラクタさえまったく持つ必要がない。
  2. クラスが、シリアライズ可能である必要がない。
  3. クラスが、setterメソッドを持つ必要がない(少なくともデータ・バインドに関してはgetterメソッドのみ留意される)。
  4. メソッドだけではなく、アクセス可能なpublicフィールドもサポートされるため、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のようなレガシー・クラスをサポートできることを意味しています。

  5. JavaBeansではメソッドだけでなく、クラス自体がpublicになることが必要です。良い設計の原則でこれらのクラスをpublicにしないように推奨される場合があるため、この制限事項には少々問題があります。UIXの内部変換で次の代替方法をサポートします。