Top / Eclipse / プラグイン開発のTIPS集 / org.eclipse.core.runtime.IAdaptable(cont.)2

ここまでで

Eclipse/プラグイン開発のTIPS集/org.eclipse.core.runtime.IAdaptable(cont.)まで数回、IAdaptableについて学んできました。最後に

org.eclipse.core.runtime.IAdaptable インタフェース
org.eclipse.core.runtime.PlatformObject オブジェクト
org.eclipse.core.runtime.IAdapterFactory ファクトリ

の機構のまとめとその応用をまとめたいと思います。

Viewerへのモデルオブジェクトの表示で、PlatformObject?を用いた方法を示しました。さらにモデルオブジェクトがPlatformObject?でないモデル群に対して、動的にgetAdapterする拡張ポイントの仕組みの学習をしました。

次の題材はEclipse/プラグイン開発のTIPS集/プロパティシートを使うでやったモデルオブジェクトのプロパティビューへの表示をやりたいと思います。

このプロパティビューへの表示ですが、一般的には

  • モデルのオブジェクトがIPropertySource?を実装していること
  • モデルのオブジェクトがIAdaptableを実装していて、IPropertySource?を返すこと

を要求されています。が、これって動的にやることでモデルオブジェクトにEclipseの型を導入しなくてもいけるんでないの?というところを確認したいと思います。

プロパティビューのまとめ

詳細はEclipse/プラグイン開発のTIPS集/プロパティシートを使うに記載しましたが、いわゆるビュー(ViewPart?のサブクラス)で、

public Object getAdapter(Class adapter) {
  if (adapter.equals(IPropertySheetPage.class)) {
    return new PropertySheetPage();
  }
  return super.getAdapter(adapter);
}

とOverrideしておくと、ビュー内のモデルとなるオブジェクトを選択するとプロパティビューが反応して、プロパティビューに値が表示される仕組みです。

PropertySheet?クラス(まさにプロパティビュー)は

public void init(IViewSite site) throws PartInitException {
  site.getPage().addSelectionListener(this);
  super.init(site);
}

と初期化時にpageにaddSelectionListener?され、

public void dispose() {
  // run super.
  super.dispose();
  // remove ourselves as a selection listener
  getSite().getPage().removeSelectionListener(this);
}

とdispose時にpageからremoveSelectionListener?されてます。このようにして、ビュー内の選択を検知しています。

さてあるビューでモデルオブジェクトを選択したときに、モデルオブジェクトから値をとりだしてプロパティビューに値がセットされますが、この処理はPropertySheetEntry?というクラスの

protected IPropertySource getPropertySource(Object object);

というメソッドが行っています。何とかしてIPropertySource?を返す、そんなメソッドです。

具体的には

↓ViewPart(id: org.eclipse.ui.views.PropertySheet まさにプロパティビュー)
PropertySheet#selectionChanged -> PropertySheetPage#selectionChanged 
  -> PropertySheetViewer#setInput -> PropertySheetEntry#setValues 
  -> PropertySheetEntry#getPropertySource <-このメソッドの返り値がIPropertySource

という流れ。なげー。*1

このメソッドでIPropertySource?を取得し、プロパティビューへの描画が行われます。

プロパティビューの処理の詳細

なぜモデルがIAdaptableだったりIPropertySource?を実装しないといけないのか

さて、 モデルオブジェクトはIAdaptableだったりIPropertySource?を実装することが必要だったわけですが、実はこのPropertySheetEntry?#getPropertySource?メソッドで

protected IPropertySource getPropertySource(Object object) {
  if (sources.containsKey(object))
    return (IPropertySource) sources.get(object);
  IPropertySource result = null;
  if (propertySourceProvider != null)
    result = propertySourceProvider.getPropertySource(object);
  else if (object instanceof IPropertySource)
    result = (IPropertySource) object;
  else if (object instanceof IAdaptable)
    result = (IPropertySource) ((IAdaptable) object)
        .getAdapter(IPropertySource.class);
  sources.put(object, result);
  return result;
}

などとやってるからなんですね。モデル(objectインスタンス)からデータを取り出すときに、モデルがIPropertySource?だったらそのまま返す、もしくはモデルがIAdaptableの場合はgetAdapter(IPropertySource?.class);して返す、などなどして、IPropertySource?インスタンスを取得しています。

IAdaptableでもIPropertySource?でもないモデルに対しては

さて今回の目的は、IAdaptableでもIPropertySource?でもないモデルオブジェクトをビューにセットして、プロパティビューを反応させることでした。残る道は先のメソッドの

IPropertySource result = null;
if (propertySourceProvider != null)
  result = propertySourceProvider.getPropertySource(object);

ですね。

ではこのpropertySourceProvider?のセンで調べてみます。Eclipseのソースを見てるとどうやら、ビューのgetAdapterで何気なしに返していた、org.eclipse.ui.views.properties.PropertySheetPage?

public void setPropertySourceProvider(IPropertySourceProvider newProvider);

でこのpropertySourceProvider?を設定できそうです。これを呼ぶと巡り巡ってPropertySheetEntry?クラスのフィールドpropertySourceProvider?にnewProviderがセットされます。つまりビューのgetAdapterでreturn new PropertySheetPage?();しているところで、このセッタメソッドsetPropertySourceProvider?()を呼んで、IPropertySourceProvider?のインスタンスをPropertySheetEntry?にセットすれば良さそうです。

で、IAdaptableの話

ここまでで、ビューのgetAdapterメソッドを

public Object getAdapter(Class adapter) {
  if (adapter.equals(IPropertySheetPage.class)) {
    // return new PropertySheetPage();
    PropertySheetPage page = new PropertySheetPage();
    page.setPropertySourceProvider(hogehoge);
    return page;
  }
  return super.getAdapter(adapter);
}

などと実装すれば、モデルオブジェクトをIAdaptableやIPropertySource?にしなくともhogehogeというIPropertySourceProvider?からIPropertySource?を取得してくれそう、というところまでわかりました。

ここでようやく、IAdaptableをいろいろ調べてた苦労が実りそうです。

public Object getAdapter(Class adapter) {
  if (adapter.equals(IPropertySheetPage.class)) {
    // return new PropertySheetPage();

    PropertySheetPage page = new PropertySheetPage();
    page.setPropertySourceProvider(new IPropertySourceProvider() {

      public IPropertySource getPropertySource(Object object) {
        if (object instanceof Model) {
          Model model = (Model) object;
          IPropertySource adapter = (IPropertySource) Platform
              .getAdapterManager().loadAdapter(model,
                  IPropertySource.class.getName());
              ↑ ModelのインスタンスmodelとIPropertySource.class
              ↑ の組み合わせでオブジェクトをローディング
              ↑ adapterの拡張ポイントで登録が必要。
          return adapter;
        }
        return null;
      }

    });
    return page;
  }
  return super.getAdapter(adapter);
}

このように実装し*2、さらにorg.eclipse.core.runtime.adapters 拡張ポイントとIAdapterFactory?の実装クラスを定義しておきます。

拡張ポイント

<extension point="org.eclipse.core.runtime.adapters">
  <factory adaptableType="nu.mine.kino.plugin.samples.model.Model"
           class="nu.mine.kino.plugin.samples.model.PropertySourceAdapterFactory">
    <adapter type="org.eclipse.ui.views.properties.IPropertySource"/>
  </factory>
</extension>

IAdapterFactory?の実装クラス

public class PropertySourceAdapterFactory implements IAdapterFactory {
  private static final Class[] Types = new Class[] { IPropertySource.class };
  public Object getAdapter(Object adaptableObject, Class adapterType) {
    if (adapterType == Types[0] && adaptableObject instanceof Model) {
      final Model model = (Model) adaptableObject;
      IPropertySource source = new IPropertySource() {

        public IPropertyDescriptor[] getPropertyDescriptors() {
          IPropertyDescriptor[] descriptor = new IPropertyDescriptor[] {
              new TextPropertyDescriptor("id", "コード"),
              new TextPropertyDescriptor("name", "名前"),
              new TextPropertyDescriptor("mail", "メール") };
          return descriptor;
        }

        public Object getPropertyValue(Object id) {
          if (id.equals("id")) {
            return model.getId();
          }
          if (id.equals("name")) {
            return model.getName();
          }
          if (id.equals("mail")) {
            return model.getMail();
          }
          return null;
        }
        public boolean isPropertySet(Object id) {return false;}
        public void resetPropertyValue(Object id) {}
        public void setPropertyValue(Object id, Object value) {}
        public Object getEditableValue() {return null;}
      };
      return source;
    }
    return null;
  }
  public Class[] getAdapterList() {
    return Types;
  }
}

以上で完成です。ビュー上で、モデルオブジェクトを選択すると、プロパティビューが反応して、値を表示してくれることがわかります。モデルオブジェクトはプロパティシートを使うで使用したModelクラスです。これはIAdaptableでもないし、PlatformObject?を拡張もしていないいわゆるPOJOです。

pic02.png

よくよく考えたら、この例ではこんなややこしいこと必要なしだった。。。

今回の場合はこんなややこしいこと必要なかったです。

public IPropertySource getPropertySource(Object object) {
  if (object instanceof Model) {
    Model model = (Model) object;
    IPropertySource adapter = (IPropertySource) Platform
        .getAdapterManager().loadAdapter(model,
            IPropertySource.class.getName());
        ↑ ModelのインスタンスmodelとIPropertySource.class
        ↑ の組み合わせでオブジェクトをローディング
        ↑ adapterの拡張ポイントで登録が必要。
    return adapter;
  }
  return null;
}

なんてことをやりましたが、ここは

public IPropertySource getPropertySource(Object object) {
  if (object instanceof Model) {
    Model model = (Model) object;
    return new IPropertySourceImpl(model);
  }
  return null;
}

などとしておくだけで十分でした。IPropertySourceImpl?は適当に

private class IPropertySourceImpl implements IPropertySource {
  private Model model;
  public IPropertySourceImpl(Model model) {
    this.model = model;
  }
  public IPropertyDescriptor[] getPropertyDescriptors() {
    IPropertyDescriptor[] descriptor = new IPropertyDescriptor[] {
      new TextPropertyDescriptor("id", "コード"),
      new TextPropertyDescriptor("name", "名前"),
      new TextPropertyDescriptor("mail", "メール") };
    return descriptor;
  }
  public Object getPropertyValue(Object id) {
    if (id.equals("id")) {
    return model.getId();
    }
    if (id.equals("name")) {
    return model.getName();
    }
    if (id.equals("mail")) {
    return model.getMail();
    }
    return null;
  }
  public boolean isPropertySet(Object id) {return false;}
  public void resetPropertyValue(Object id) {}
  public void setPropertyValue(Object id, Object value) {}
  public Object getEditableValue() {return null;}
};

でOKです。これでorg.eclipse.core.runtime.adapters 拡張ポイントやIAdapterFactory?の実装クラスも必要なかったです。

。。。何やっってたんだー(´д`;)。。。。まあ、org.eclipse.core.runtime.adapters拡張ポイントの使い方もわかったし、まあよしとするか。。


この記事は

選択肢 投票
おもしろかった 12  
そうでもない 0  

Top / Eclipse / プラグイン開発のTIPS集 / org.eclipse.core.runtime.IAdaptable(cont.)2

現在のアクセス:10483


*1 ちなみにPropertySheet?はISelectionListener?を実装しています
*2 ここではModel型かどうかとかやってますが、本当はModelかどうかなんて意識する必要なしですね。拡張ポイントでクラスとファクトリがペアで登録されるし、ファクトリ内でちゃんと型チェックを行えば、ここはさらにポータブルにできる。というかそうした方がよいと思う。

添付ファイル: filepic02.png 831件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-09-29 (金) 15:58:00 (658d)