« 2004年5月 | トップページ | 2004年7月 »

2004.06.11

イメージレジストリ

イメージの扱い方」で紹介したように,イメージリソースは利用されなくなったら速やかにdisposeメソッドを呼び出して解放してあげなければならない。しかし,CやC++のfreeやdeleteなどのメモリ割り当ての解放作業をしたくないがためにJavaのGC(ガーベージコレクション)ができたように,イメージリソースに関しても勝手に解放してくれるような機構が欲しくなるものである。また,イメージリソースは複数の利用箇所で同一リソースを共有することができるということを考えると,実はイメージリソースの解放のタイミングは一概に決められない,といった状況も発生してくる。

そこでEclipseでは,イメージリソースを一括管理してくれるImageRegistryクラスが提供されている。ImageRegistryクラスを使うと,イメージの登録と取得,登録されたイメージの自動一括解放ができるようになる

  // レジストリの準備
  ImageRegistry registry = new ImageRegistry();

  // イメージの登録
  ImageDescriptor descriptor = ...; // 「イメージの扱い方」参照
  registry.put("smile", descriptor);

  // イメージの取得
  Image image = registry.get("smile");

使い方としては,まずImageRegistryクラスのインスタンスを生成する。そして「イメージの扱い方」で取り上げたImageDescriptorオブジェクトを生成し,putメソッドを使ってImageRegistryオブジェクトを登録する。この際,イメージの名前(何でも良い)をつけておく。

そしてイメージを利用したいときには,ImageRegistryオブジェクトからgetメソッドに登録時につけた名前を渡すことで,イメージが自動的に生成されてImageオブジェクトを得ることができる。過去にgetメソッドで取得したイメージについては,新たにイメージリソースが作成されるのではなく,内部で保持されているイメージリソースが使いまわされる

ImageRegistryクラスには,getメソッドとputメソッドしかない。登録されたイメージの破棄に関しては,完全自動化が実現されている。Imageオブジェクトの生成には,Deviceオブジェクト,通常はDisplayオブジェクトが必要となる。ImageRegistryクラスのコンストラクタ(引数なし版)では,コンストラクタを呼び出した処理のスレッドに紐づくDisplayオブジェクトをDisplay.getCurrent()で取得し,内部に保持している。そして,そのDisplayオブジェクトが破棄されたタイミングで,登録されているImageオブジェクトのdisposeメソッドを呼び出して破棄している。この処理は,DisplayオブジェクトのdisposeExecメソッドにRunnbaleオブジェクトの形でImageRegistryオブジェクトが内部で登録している。

ImageRegistryクラスを使えば,イメージリソースは勝手に破棄される。しかし,やはりイメージリソースの無駄遣いは避けるべきである。ImageRegistryクラスの使用するしないに関わらず,イメージリソースに関しては注意深く扱うべきである。

| | コメント (0) | トラックバック (0)

2004.06.10

イメージの扱い方

GUIを伴うアプリケーションを作っていると,どうしても見た目に凝りだすのは仕方のないこと。たいていはアイコンやイメージを作ってGUIに貼り付けることを考えるのではないだろうか。実はアイコンやイメージは,OSから見るとちょっとした重いリソースであり,気にしなければならないことがいくつか出てくる。今回は,Eclipseでイメージを扱うためにはどうしたら良いか,について紹介する。

イメージはgifやjpegなどの形式のバイト配列が実体だが,それはファイルとして保存されていたり,データベースにBLOGとして格納されていたりする。または,どこかのHTTPサーバに配置されているかもしれない。もちろん,それぞれでバイト配列の取得方法に違いが出てくるし,イメージの形式によっても処理内容が違ってくる。

Eclipseでは,イメージリソースの生成を統一化された方法で行うために,ImageDescriptorクラスが提供されている。ImageDescriptorクラスは,ファイル名あるいはURLにより特定される場所から,イメージリソースを生成する方法を知っているクラスである。例えば,あるプラグインのiconsフォルダに存在するsmile.gifファイルからイメージリソースを生成するためのコードは以下のようになる。

  ImageDescriptor descriptor;
  try {
    URL url = MyPlugin.getInstance().getDescriptor().getInstallURL();
    descriptor = ImageDescriptor.createFromURL(new URL(url, "icons/smile.gif"));
  } catch(MalformedURLException e) {
    descriptor = ImageDescriptor.getMissingImageDescriptor();
  }
  Image image = desriptor.createImage();

まず,Pluginクラスのインスタンスを取得し,getDescriptorメソッドでIPluginDescriptorオブジェクトを取得する。そしてIPluginDescriptorオブジェクトのgetInstallURLメソッドを呼び出すことで,プラグインがインストールされている場所を示すURLオブジェクトを得る。これは「Pluginクラスの作成」を参照されたし。

そしてImageDescriptorクラスのcreateFromURLクラスメソッドにイメージの場所を示すURLオブジェクトを渡すことで,ImageDescriptorオブジェクトを取得する。上記では,プラグインがインストールされた場所を示すURLオブジェクトに新たにicons/smile.gifというパスを追加したURLオブジェクトを生成し,createFromURLメソッドに渡すことでImageDescriptorオブジェクトを生成している。その後,createImageメソッドを呼び出すことで,イメージリソースを表すImageオブジェクト(SWTのクラス)を生成する

上記のコードの場合,もしかしたらイメージの場所を示すURLの生成に失敗するかもしれない。その場合,MalformedURLException例外が発生するが,プラグイン的には何らかのイメージが表示されないと都合が悪いこともある。そんなときのために,ImageDescriptorクラスには「イメージの読み込み,ミスったぞ」状態を表すイメージを生成するためのgetMissingImageDescriptorメソッドが用意されている。これを使うと,「missing-image.gif」というイメージリソースがcreateImageメソッドで得られる。

あとはいろんな場所で好きなようにImageオブジェクトを利用すればよい。

さて,イメージリソースはその使用が終わったときには,ちゃんと破棄しなければならない。冒頭で述べたとおり,イメージリソースは重いので,必要最低限のイメージリソースの確保時間にとどめる事がプラグイン開発者に求められる。さもないと,他のプラグイン,しいてはEclipse全体のパフォーマンスに影響を及ぼしかねない。

イメージリソース,つまりImageオブジェクトの破棄は,

  Image image = ...;
  image.dispose();

というようにdisposeメソッドを呼び出すだけである。非常に簡単。いかに必要最低限の確保にとどめ,最適なタイミングでdisposeするか,はプログラマの腕の見せ所だし,楽しく感じる部分である(特にGCに頼り切っているJavaしか知らない開発者は新鮮だろう)。タイミングという点では,JFaceの各種Viewerでは,Viewerが破棄されるときにViewerのdisposeメソッドが呼び出されるので,その中で使用したイメージリソースの破棄を行えばよいし,その他必ずイメージリソースを破棄できるタイミングは存在するはずなので,忘れずにdisposeするように心がけたい。間違っても「Pluginクラスのshutdownメソッド内で一気に破棄すればいいじゃん」なんて横着なことは考えないように。

| | コメント (0) | トラックバック (0)

2004.06.09

Pluginクラスの作成

Eclipseプラグインは,各プラグインの生成と破棄のタイミング(ライフサイクル)で何らかの処理を行うために,Pluginクラスを準備しておくことができる。各プラグインでは,Pluginクラスのサブクラスを作成しておき,プラグイン・マニフェストにそのクラス名を記載しておくことによって,Eclipseプラットフォームが自動的にPluginクラスのインスタンスを生成し,所定のメソッドを呼び出してくれる

  public class MyPlugin extends Plugin {
    public MyPlugin(IPluginDescriptor descriptor) {
      super(descriptor);
    }
    public void startup() throws CoreException {
      // プラグイン活性時に行いたい処理
    }
    public void shutdown() throws CoreException {
      // プラグイン非活性時に行いたい処理
    }
  }

プラグイン・マニフェストでは,plugin要素のclass属性にPluginクラス名を指定する。

  <?xml version="1.0" encoding="UTF-8"?>
  <plugin
      id="yoichiro.myPlugin"
      name="Yoichiro Plugin"
      version="1.0.0"
      class="yoichiro.myPlugin.MyPlugin">
    ...
  </plugin>

Pluginクラスの典型的な作りとして,自身のインスタンスを返却するクラスメソッドを準備するということがあげられる。Pluginオブジェクトは,結構いろんな箇所で使われることになる。代表的な例としては,プラグインのインストールされている場所を取得するために使ったりする。ソースコードはこんな感じ。

  public class MyPlugin extends Plugin {
    private static MyPlugin instance;
    public MyPlugin(IPluginDescriptor descriptor) {
      super(descriptor);
      instance = this;
    }
    public static MyPlugin getInstance() {
      return instance;
    }
  }

コンストラクタでインスタンスをフィールドに保持して,getInstanceメソッドで取得できるようにしている。これにより,プラグイン内の任意のクラスから,

  URL url = MyPlugin.getInstance().getDescriptor().getInstallURL();

で,プラグインがインストールされている場所を取得することができる。

インストール場所の取得以外にも,Pluginクラスは言わばコントローラ的役割(処理の起点)を持たせたりもするので,上記のようにgetInstanceメソッドでインスタンスを何処からでも取得できるようにしておくと非常に有効である。

| | コメント (0) | トラックバック (0)

2004.06.05

ファイルの作成

ほとんどのソフトウェアが必ずといっていいほど行っている処理として,ファイルの作成があげられる。一番手軽な情報の永続化の方法である。今回は,プラグイン内で,あるプロジェクトまたはフォルダにファイルを作成するための方法を紹介する。

Eclipseでは,ファイルなどのリソースは「ハンドル」と呼ばれる識別子で表される。ハンドルはリソースの実体に関する情報を持つものだが,実体がなくても(あたかも実体があるかのように)作成することができる。ファイルの作成は,作成したいファイルのハンドルを作成し,そのハンドルに対して作成を指示する,という手順になる。

まずはソースコードを紹介しよう。

  IContainer container = ...; // IProjectまたはIFolderオブジェクト

  IFile file = container.getFile(new Path("hoge.txt"));

  String contents = "Hello world!";
  InputStream is = new ByteArrayInputStream(contents.getBytes());

  file.create(is, false, null);

ファイルはプロジェクトまたはフォルダに対して作成される。つまり,まずはプロジェクトを表すIProjectオブジェクトか,フォルダを表すIFolderオブジェクトを何らかの方法で取得する。この2つのインタフェースは,共にIContainerインタフェースを継承している。IContainerインタフェースには,ファイルハンドルを生成するためのgetFileメソッドが規定されているので,それを利用して作成したいファイルのハンドルオブジェクト(IFileオブジェクト)を生成する

getFileメソッドの引数には,ファイルのパスを表すIPathインタフェースのオブジェクトを指定する。EclipseではIPathインタフェースの実装クラスとしてPathクラスを用意してくれているので,Pathクラスのコンストラクタに作成したいファイルのファイル名を渡してインスタンスを生成している。ファイルのパスはcontainerオブジェクトが持つファイルシステム上の場所からの相対パスになる。

IFileオブジェクトによるファイルの作成には,作成するファイルの内容を入力ストリームの形で渡さなければならない。そこで上記のソースコードでは,”Hello world!”という文字列をバイト列に変換し,そのバイト列を元にByteArrayInputStreamオブジェクトを生成してファイルの内容としている。

そしてハンドルの実体,つまりファイルの作成はIFileオブジェクトのcreateメソッドを使って行う。第1引数に先ほど作成しておいた入力ストリームを渡し,第3引数にIProgressMonitorオブジェクトを渡す。第1引数の内容によってはファイルの作成に長時間かかる可能性があるため,進捗表示を行うための機能がcreateメソッドに備わっている。

第2引数だが,これはちょっと難しい。createメソッドの挙動を決定するためのフラグなのだが,基本的に第2引数は作成しようとしたファイルが既に存在したときにどうするか,ということを指定する。このフラグも含めて,createメソッドの挙動をまとめてみる。

  ・「hoge.txt」が存在し,既にプロジェクトに登録されていたとき
    第2引数の値に関係なく,エラー(Resource ... already exists.)が発生。

  ・「HOGE.TXT」が存在し,既にプロジェクトに登録されていたとき
    第2引数の値に関係なく,エラー(A resource exists with a different case: ...)が発生。

  ・「hoge.txt」が存在し,プロジェクトに登録されていないとき
    第2引数がtrueの場合,一旦削除されて新しく作成される
    第2引数がfalseの場合,エラー(A resource already exists on disk ...)が発生。

  ・「HOGE.txt」が存在し,プロジェクトに登録されていないとき
    第2引数の値に関係なく,エラー(A resource exists on disk with a different case: ...)が発生。

  ・「hoge.txt」「HOGE.TXT」が存在しないとき
    第2引数の値に関係なく,新しく作成される

安全性を求めるなら,第2引数はfalseを指定しておくのが無難だろう。以上の処理により,ファイルが作成されてプロジェクトに登録される。

| | コメント (0) | トラックバック (0)

« 2004年5月 | トップページ | 2004年7月 »