ClientBundle その2

ClientBundleも以前に紹介した国際化の時同様に、GWTの様式である

1. 用途に合わせた(ClientBundle)インタフェース作成
2. 必要なオブジェクトを取得するためのアクセサ定義
3. アプリケーション中でClientBundleの実体化

という手順で使用する。

ClientBundleインタフェース作成

ClientBundleインタフェースを拡張してその中にアプリケーションで使用するリソースを包含するようなインタフェースを作成する。

  • AddressBookResource.java
public interface AddressBookResources extends ClientBundle {
    public static final AddressBookResources INSTANCE = 
        GWT.create(AddressBookResources.class);
    :
    リソースを取得するためのアクセサ1
    リソースを取得するためのアクセサ2
    :
}

public static finalなフィールドにインスタンスを生成してセットしておくのは、GWTによく見られる一種のシングルトンパターンだ。

AddressBookResources res = AddressBookResources.INSTANCE;

別に定義しなくても問題は無いが、このように都度インスタンスを生成せずに参照するためのイディオムである。

必要なオブジェクトを取得するためのアクセサ定義

ClientBundleがアクセサとして扱えるリソースの型は ResourcePrototype及びResourceGeneratorインタフェースを実装したジェネレータ型に依存するが、一般的なリソース型の実装が既に提供されており通常はこれらを使うだけで良い。

  • ImageResource : javax.imageioでサポートするイメージデータを提供する(jpg, png, bmp, wbmp, gif)
  • CssResource : CSSスタイルシートを提供する
  • TextResource : 内部埋め込み又は個別ファイルに格納されるテキストデータを提供する
  • DataResource : テキスト以外のデータを提供する
  • GWTCreateResource : GWT.create()メソッドによって生成されたリソース型を提供する
  • ClientBundleの派生型 : ネストしたClientBundle

以下一般的なものだけ紹介する。

ImageResource

参照されるイメージを取得するためのアクセサの戻り値として定義する。アクセサは通常リソース名(ファイルのベース名)と同じにするが、変更する場合は@Sourceアノテーションで実際のリソースのファイル名を記述する。

    // splash.gifを取得する
    ImageResource splash();

    @Source("icon.png")
    ImageResource favicon();
CssResource

参照されるCSSを取得するためのアクセサの戻り値として定義する。アクセサはCSSリソース名(ファイルのベース名)と同じにするが、変更する場合は@Sourceアノテーションで実際のリソースのファイル名を記述する。

CssResource自体はGWTコンパイル時にCSS内に記述されたセレクタ全てをメソッドとして定義したインタフェースを実装したものである必要がある。

例えばこのようなスタイルシートをCssResourceを戻すアクセサとして定義した場合、

  • AddressBook.css
#loading {
     display: block;
     position: absolute;
     top: 50%;
     left: 50%;
     text-align: center;
     margin-left: -100px;
  }
div, .contentContainer{
    margin: 5px;
  }

idセレクタであるcontentContainerをアクセサとして使用する、以下のインタフェースを別途用意する必要がある。

public interface AddressBookCSS extends CssResource {
    String contentContainer();
}

ClientBundleでは、このAddressBookCSSを戻すアクセサとして定義するのである。

  • AddressBookResource.java
public interface AddressBookResources extends ClientBundle {
    public static final AddressBookResources INSTANCE = 
        GWT.create(AddressBookResources.class);

    // splash.gifを取得する
    ImageResource splash();

    @Source("icon.png")
    ImageResource favicon();

    //AddressBook.cssを取得する
    AddressBookCSS addressBookCSS();

このように使用するスタイルシートに対してインタフェースを用意しないとGWTコンパイル時に以下のようにエラーになってしまう。

Compiling module net.kazzz.AddressBook
   Scanning for additional dependencies: file:/E:/gwt/project/addressbook/src/net/kazzz/client/AddressBook.java
      Computing all possible rebind results for 'net.kazzz.client.AddressBookResources'
         Rebinding net.kazzz.client.AddressBookResources
            Invoking generator com.google.gwt.resources.rebind.context.InlineClientBundleGenerator
               Creating assignment for css()
                  Replacing CSS class names
                     [ERROR] The following unobfuscated classes were present in a strict CssResource:
                        [ERROR] contentContainer
                        Fix by adding String accessor method(s) to the CssResource interface for obfuscated classes, or using an @external declaration for unobfuscated classes.
               [ERROR] Generator 'com.google.gwt.resources.rebind.context.InlineClientBundleGenerator' threw an exception while rebinding 'net.kazzz.client.AddressBookResources'
com.google.gwt.resources.css.ast.CssCompilerException: Missing a CSS replacement

GWTコンパイルの際にスタイルシートに対して上記のように厳密な参照のチェックとアクセサの有無を検査するのが嫌なのであれば、アノテーション@CssResource.NotStrictを使用して定義することでエラーを抑制することもできる。

    //AddressBook.cssを取得する
    @CssResource.NotStrict
    CssResource addressBookCSS();

また、様々なアノテーションCSS中に記述することで、GWTコンパイル時のCSSパース時様々な処理を行うこともできるが、非常に多くの種類があるのでここでは割愛する。

TextResource

参照されるテキストデータを取得するためのアクセサの戻り値として定義する。アクセサはリソース名(ファイルのベース名)と同じにするが、変更する場合は@Sourceアノテーションで実際のリソースのファイル名を記述する。

その記述方法はImageResourceと同様なので割愛するが、たくさんのテキストデータがあって、いちいちファイル毎にアクセサを追加していくのが面倒な場合、Eclipseであればプラグインが提供するウィザードで一気にデータを追加することができる。


→コンテキストメニュー→Google→Add to ClientBundleから


ウィザード中で追加したいデータをプロジェクトツリーから複数選択する

OKボタン押下後、AddressBookResourcesには以下の行が追加されているはずだ。

  • AddressBookResources.java
    @Source("net/kazzz/client/text/a.txt")
    TextResource a();

    @Source("net/kazzz/client/text/b.txt")
    TextResource b();

    @Source("net/kazzz/client/text/c.pdf")
    DataResource c();

    @Source("net/kazzz/client/text/d.xml")
    DataResource d();

テキストデータだからといって拡張子が.txtである必要は無いが、ウィザードで処理するとこのようにDataResourceとして処理されてしまうようだ。