ゆーたんのつぶやき

株式会社ノークリサーチにてIT関連のシニアアナリストとして活動しています。

JFaceのImageRegistry



SWTでアプリケーションを開発する場合に厄介なのは画像データの扱いです。
作成したイメージは明示的にdispose()してあげないとゴミが残ります。
これらの手間を省くためにJFaceではImageRegistryクラスが提供されています。
アプリケーション内で利用する画像データをあらかじめImageRegistryに登録
しておいて必要な時に呼び出します。各画像データのdispose()をプログラマ
がコーディングする必要はありません。


コーディングは下記のような感じになります。
(簡単のため例外処理などは省略しています)
[ImageRegistryをラップしたユーティリティクラス]


public class ImageUtil {

private static ImageRegistry registry;

public static ImageRegistry getImageRegistry(){
if(registry == null){
registry = new ImageRegistry();
registry.put("open",
ImageDescriptor.createFromURL(new URL("file:icons/open.gif")));
registry.put("close",
ImageDescriptor.createFromURL(new URL("file:icons/close.gif")));
return registry;
}

[簡単なJFaceアプリケーションの例]

public class MyWindow extends ApplicationWindow{

private OpenAction open_action;

public MyWindow(){
super(null); -------(※1)
open_action = new OpenAction(this);
addMenuBar();
}

protected MenuManager createMenuManager(){
MenuManager menu = new MenuManager("");
MenuManager open_menu = new MenuManager("&Open");
menu.add(open_menu);
open_menu.add(open_action);
return menu;
}

[上記のアプリケーションで呼び出されるアクション]

public class OpenAction extends Action

MyWindow parent;

public OpenAction(MyWindow parent){
this.parent = parent;
setText("開く");
setImageDescriptor(ImageUtil.getImageRegistry().getDescriptor("open"));
}

public void run(){
//実際の処理内容はここに書く
}
}

一見これで良さそうにも思いますが、実行するとエラーになります(T_T)
ImageRegistryオブジェクトはDisplayが生成された後でないと使えないからです。
じゃあ、※1の箇所を「super(new Shell(new Display()))」とすればいいんじゃない?
ということでやってみると、とりあえず動くことは動きます。ですが、今度はWindowクラス
の仕様として「createMenuManager()メソッドはShellの生成前に実行しなければならない」
という注意事項があります。SWTであればDisplayとShellの生成は明示的に行ないますので
そのあたりの細かい制御は可能ですが、そうするとJFaceの多くのメリットが生きなくなって
しまいます。つまり、「JFaceでActionに設定するアイコン画像などをImageRegistryで管理
する」ということをやろうとするとこうした問題に直面するわけです。


解決策を模索したところ、メニューの生成処理部分をconfigureShell()メソッド内に記述
すればよいことがわかりました。このメソッドはShell自体にレイアウトを設定するなどの
目的のためにアプリケーション側でオーバーライドするものとして提供されています。
そうすると、コードは下記のようになります。
[簡単なJFaceアプリケーションの例(改訂版)]

public class MyWindow extends ApplicationWindow{

private OpenAction open_action;

public MyWindow(){
super(null); -------(※1)
}

protected void configureShell(Shell shell){
super.configureShell(shell);
open_action = new OpenAction(this); -------(※2)
addMenuBar();
}

protected MenuManager createMenuManager(){
MenuManager menu = new MenuManager("");
MenuManager open_menu = new MenuManager("&Open");
menu.add(open_menu);
open_menu.add(open_action);
return menu;
}

※2の段階ではDisplayは存在しますが、Shellはまだ設定されていません。
実際、※2の箇所でgetShell()メソッドを呼んでもnullが返ってきます。
ということで、上記のようなコードであれば
・ImageRegistryを使用するにはDisplayが生成されていることが必要
・メニュー生成処理はShellが生成される前に行なわなければならない
という二つの条件をクリアすることができます。ちなみにメニューに関
する諸所の事項はツールバーやステータスについても当てはまります。


どのようなフレームワークでもそうですが、アプリケーション側で独自
に作成した部品を組み込むためのインターフェースやユーティリティが
どの段階で利用可能になるのか?を把握しておくことは重要ですね。
フレームワークというとどうしても用意されているAPI仕様に目が行って
しまいがちですが、複雑に絡み合ったフレームワーク内部のオブジェクト
のライフサイクルについてもある程度把握しておくことも大切だ、という
ことをあらためて実感しました。