// 下階層用テンプレート
#topicpath
----
#contents

**ここまでで [#j466e50d]
//ここにコンテンツを記述します。
[[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へのモデルオブジェクトの表示>Eclipse/プラグイン開発のTIPS集/org.eclipse.core.runtime.IAdaptable#r6a0a2c1]]で、PlatformObjectを用いた方法を示しました。さらにモデルオブジェクトがPlatformObjectでないモデル群に対して、[[動的にgetAdapterする拡張ポイントの仕組み>Eclipse/プラグイン開発のTIPS集/org.eclipse.core.runtime.IAdaptable(cont.)]]の学習をしました。

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

このプロパティビューへの表示ですが、一般的には
-モデルのオブジェクトがIPropertySourceを実装していること
-モデルのオブジェクトがIAdaptableを実装していて、IPropertySourceを返すこと


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

**プロパティビューのまとめ [#a415cb53]
詳細は[[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
という流れ。なげー。((ちなみにPropertySheetはISelectionListenerを実装しています))

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


**プロパティビューの処理の詳細 [#edc2ceff]
***なぜモデルがIAdaptableだったりIPropertySourceを実装しないといけないのか [#xcb0142f]
さて、
モデルオブジェクトは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でもないモデルに対しては [#z94d8e3b]
さて今回の目的は、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の話 [#y1ae2f56]
ここまでで、ビューの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);
 }
このように実装し((ここではModel型かどうかとかやってますが、本当はModelかどうかなんて意識する必要なしですね。拡張ポイントでクラスとファクトリがペアで登録されるし、ファクトリ内でちゃんと型チェックを行えば、ここはさらにポータブルにできる。というかそうした方がよいと思う。))、さらにorg.eclipse.core.runtime.adapters 拡張ポイントとIAdapterFactoryの実装クラスを定義しておきます。

***拡張ポイント [#l9655303]
 <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の実装クラス [#i39ffccf]
 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;
   }
 }

以上で完成です。ビュー上で、モデルオブジェクトを選択すると、プロパティビューが反応して、値を表示してくれることがわかります。モデルオブジェクトは[[プロパティシートを使う>Eclipse/プラグイン開発のTIPS集/プロパティシートを使う#cd2098e7]]で使用したModelクラスです。これはIAdaptableでもないし、PlatformObjectを拡張もしていないいわゆるPOJOです。




#ref(pic02.png)



**よくよく考えたら、この例ではこんなややこしいこと必要なしだった。。。 [#d9f8be71]
今回の場合はこんなややこしいこと必要なかったです。
 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拡張ポイントの使い方もわかったし、まあよしとするか。。




----
この記事は
#vote(おもしろかった[11],そうでもない[0])
#vote(おもしろかった[12],そうでもない[0])

#comment
#topicpath


SIZE(10){現在のアクセス:&counter;}

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS