GWTと遅延バインディング(deferred binding) その2

さて、昨日の続き。
GWTの遅延バインディングには2つの方法がある。

遅延バインディングの方法

  • 置換

設定可能なルールに応じて型が置換される。

  • コード生成

ジェネレータの実行により型(コード)が生成される。

置換

置換(Replacement)はgwt.xmlのreplace-with要素とその子要素を使用して型の実装を置換(再定義)することができる。

  • 要素

子要素に指定する条件を設定する要素の論理が真の場合にclass属性で指定された型を置換する。

    • replace-with要素の例
<module>
  :
  <!-- old Mozilla, and Opera need a different implementation -->
  <replace-with class="com.google.gwt.user.client.ui.impl.FocusImplOld">
    <when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl" />
      <any>
        <when-property-is name="user.agent" value="gecko" />
        <when-property-is name="user.agent" value="opera" />
      </any>
  </replace-with>
</module>

この例で言えば、プロパティ"user.agent"の値が"gecko"又は"opera"の場合、com.google.gwt.user.client.ui.impl.FocusImplをcom.google.gwt.user.client.ui.impl.FocusImplOldで置き換える、という遅延バインディングのルールとなる。

コード生成

generate-with要素で指定した抽象クラスGeneratorsを実装したクラスの実行により生成された型を実装として使用する。

  • 要素

Generatorsクラスの実装クラスを宣言する。

    • generate-with要素の例
<module>
  <inherits name='com.google.gwt.user.User' />
  <generate-with class="com.gwtmobile.persistence.rebind.GWTPersistenceGenerator">
    <when-type-assignable
      class="com.gwtmobile.persistence.client.Persistable" />
  </generate-with>

</module>

com.gwtmobile.persistence.client.Persistableと互換性のある型(インタフェース)ならば、com.gwtmobile.persistence.rebind.GWTPersistenceGenerator#generateメソッドがGWTに呼ばれることにより実装を生成する。

プロパティの宣言と設定

上の例で既に登場したが、gwt.xml内ではその包含関係の中で参照できるプロパティの宣言と値の設定を行うことができる。宣言と設定にはdefine-property、property-provider、set-property要素をそれぞれ使用する。

  • 要素

プロパティの名前と値を宣言する

  • 要素

Javascriptのコードの評価結果をプロパテイの値として設定する

  • 要素

プロパテイに任意の値を設定する

    • define-propertyの例
    <!-- Add the mobile.user.agent property -->
    <define-property name="mobile.user.agent" values="mobilesafari, none"/>

    <!-- property-provider to choose mobilesafari when appropriate -->
    <property-provider name="mobile.user.agent"><![CDATA[
      var ua = navigator.userAgent.toLowerCase();
      if (ua.indexOf("webkit") != -1 && ua.indexOf("mobile") != -1) {
        return "mobilesafari";
      }
      return "none";
  ]]></property-provider>

    <!-- Make sure mobile.user.agent always has the none value for browsers other than safari. -->
    <set-property name="mobile.user.agent" value="none" >
        <none>
            <when-property-is name="user.agent" value="safari" />
        </none>
    </set-property>

何をしているかは上の例を見れば一目瞭然だが、スクリプトのコード評価をプロパテイの値に使うproperty-providerが非常に面白い。

定義条件

置換時のreplace-with要素やプロパテイ設定時のset-property要素の子要素として真偽を決定するための条件要素を複数記述できる。

  • 要素

任意の名前を持つプロパティの値で真偽を決定する

  • 要素

任意の型と互換性があるか否かで真偽を評価する

  • 要素

任意の型であるか否かで真偽を評価する

  • 要素

複数の子供の要素の評価結果をAND演算した結果で真偽を評価する

  • 要素

複数の子供の要素の評価結果をOR演算した結果で真偽を評価する

  • 要素

複数の子供の要素の評価結果をNAND演算した結果で真偽を評価する

    • 条件記述要素の例
  :
    <!-- "user.agent"の値が"safari"以外 -->
    <set-property name="mobile.user.agent" value="none" >
        <none>
            <when-property-is name="user.agent" value="safari" />
        </none>
    </set-property>

    <!-- "user.agent"の値が"safari" 且つ "mobile.user.agent"の値が"mobilesafari" -->
    <replace-with class="com.gwtmobile.ui.client.event.DragControllerMobile">
        <when-type-is class="com.gwtmobile.ui.client.event.DragController"/>
        <all>
            <when-property-is name="user.agent" value="safari"/>
            <when-property-is name="mobile.user.agent" value="mobilesafari"/>
        </all>
    </replace-with>
   :    

このようにGWTは遅延バインディングの「置換」と「コード生成」を条件に応じて適用することにより、アプリケーションのソースコードには手を入れずに様々な環境やプラットホームに対応することができる訳だ。