Eclipseプラグイン開発をする上で、避けて通ることができないのが、org.eclipse.core.runtime.IAdaptable インタフェースです。このインタフェースは
public Object getAdapter(Class adapter);
というメソッドだけが宣言されています。このメソッドはEclipseプラットフォーム側が、このインタフェースを実装したオブジェクトに対して「Class adapterに対応したモノ持ってる?もってたらちょうだい。。」てのを実現するためのメソッドとでもいえばよいでしょうか。
たとえばEclipse/プラグイン開発のTIPS集/プロパティシートを使うなどではビューやエディタに対して
public Object getAdapter(Class adapter) { if(adapter.equals(IPropertySheetPage.class)) return new PropertySheetPage(); return super.getAdapter(adapter); }
などとやって、このビューはプロパティシートを使えるよー、なんてことを指定することができるという仕組みです。通常ならプロパティシートを使えるか、○○を使えるか、などさまざまなインタフェースを定義して実装するみたいな感じになりますが、これだとただひとつの汎用的なアダプタでそれを実現することができます。まさにアダプタです。
この緩やかさ(疎さ?)が、またわかりにくいなぁっておもったりもするんだけど。
さて、このIAdaptableインタフェースですが、すべてのClass adapterに対して返すモノを考えなくてはいけないのはしんどいので、デフォルトの実装としてorg.eclipse.core.runtime.PlatformObject があるようです。でさらにそれに指示を与えるためのファクトリとしてorg.eclipse.core.runtime.IAdapterFactory があります。つまりEclipseのIAdaptableの機構を理解するには
org.eclipse.core.runtime.IAdaptable インタフェース org.eclipse.core.runtime.PlatformObject オブジェクト org.eclipse.core.runtime.IAdapterFactory ファクトリ
を理解する必要があるみたいですね。
ためしに以下のIAdaptableなクラス
class MyHashMap implements IAdaptable { private Map delegate = new HashMap(); public int size() {return delegate.size();} public Collection values() {return delegate.values();} public Object getAdapter(Class clazz) { if (clazz == List.class) { List list = new ArrayList(delegate.size()); list.addAll(delegate.values()); return list; } return null; } }
を上の3つのクラスで実現してみたいと思います。ちなみにこのクラスはListくれーてEclipseがいうとListを返すHashMap?みたいなもんです。つまり
MyHashMap map = new MyHashMap(); System.out.println(map.getAdapter(List.class).getClass().getName());
はjava.util.ArrayList?となります。まさにアダプタですね。
まず上のクラスをPlatformObject?を拡張するよう変更します。
// class MyHashMap implements IAdaptable { class MyHashMap extends PlatformObject { private Map delegate = new HashMap(); public int size() {return delegate.size();} public Collection values() {return delegate.values();} // public Object getAdapter(Class clazz) { // if (clazz == List.class) { // List list = new ArrayList(delegate.size()); // list.addAll(delegate.values()); // return list; // } // return null; // } }
つぎに、IAdapterFactory?を実装する、ListAdapterFactory?を作成します。
class ListAdapterFactory implements IAdapterFactory { public Object getAdapter(Object adaptableObject, Class adapterType) { if (adapterType == List.class && adaptableObject instanceof MyHashMap) { MyHashMap map = (MyHashMap) adaptableObject; List list = new ArrayList(map.size()); list.addAll(map.values()); return list; } return null; } public Class[] getAdapterList() { return new Class[] { List.class }; } }
以上で完成です。後は使うときに、ランタイムにこのファクトリをEclipseプラットフォームに登録します。具体的には以下の通りです。
IAdapterFactory factory = new ListAdapterFactory(); Platform.getAdapterManager().registerAdapters(factory, MyHashMap.class);
これで登録ができました。以上で、
MyHashMap map = new MyHashMap(); System.out.println(map.getAdapter(List.class).getClass().getName());
はやっぱりjava.util.ArrayList?となります。
......この例って、意味ある??使い方は何となくわかったけど、定義している場所が違うだけみたいな。。。。強いて言えば、動的にアダプタを切り替えたり、実装をこのファクトリに集約することができるとか??ちなみにプラットフォームから登録を解除するには
Platform.getAdapterManager().unregisterAdapters(factory);
でOKです。
そもそもこの例をやりたかったのでした。Viewer系のModelと2つのプロバイダで、この機構が使われていたからです。具体的にはBaseWorkbenchContentProvider?ですね。
書きとちゅー。。。。。。
BaseWorkbenchContentProvider?の実装を調べてみました。
public Object[] getChildren(Object element);
をみてみると、elementオブジェクトがIAdaptableの場合、
(IWorkbenchAdapter) ((IAdaptable) model).getAdapter(IWorkbenchAdapter.class);
とcastしてIWorkbenchAdapter?#getChildren(Object element) を呼ぶ、という実装になってます。 つまりモデルとなるelementがどのようなIWorkbenchAdapter?を持ってるか、で挙動が決まるって事ですね。
この記事は
現在のアクセス:16133