// 下階層用テンプレート #topicpath ---- #contents //ここにコンテンツを記述します。 **IAdaptableってなに [#e29fa6f9] Eclipseプラグイン開発をする上で、避けて通ることができないのが、[[org.eclipse.core.runtime.IAdaptable:http://help.eclipse.org/help31/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IAdaptable.html]] インタフェースです。このインタフェースは 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); } などとやって、このビューはプロパティシートを使えるよー、なんてことを指定することができるという仕組みです。通常ならプロパティシートを使えるか、○○を使えるか、などさまざまなインタフェースを定義して実装するみたいな感じになりますが、これだとただひとつの汎用的なアダプタでそれを実現することができます。まさにアダプタです。 この緩やかさ(疎さ?)が、またわかりにくいなぁっておもったりもするんだけど。 **PlatformObject ってなに?IAdapterFactory てなに?? [#ia7bfa63] さて、このIAdaptableインタフェースですが、すべてのClass adapterに対して返すモノを考えなくてはいけないのはしんどいので、デフォルトの実装として[[org.eclipse.core.runtime.PlatformObject:http://help.eclipse.org/help31/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/PlatformObject.html]] があるようです。でさらにそれに指示を与えるためのファクトリとして[[org.eclipse.core.runtime.IAdapterFactory:http://help.eclipse.org/help31/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IAdapterFactory.html]] があります。つまりEclipseのIAdaptableの機構を理解するには org.eclipse.core.runtime.IAdaptable インタフェース org.eclipse.core.runtime.PlatformObject オブジェクト org.eclipse.core.runtime.IAdapterFactory ファクトリ を理解する必要があるみたいですね。 **例1 [#zb5cbeac] ためしに以下の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です。 **例2 [#r6a0a2c1] ***Viewerでの使われ方 [#pe086929] Viewer系のModelと2つのプロバイダで、この機構が使われています((そもそもこれを理解するために始めたんです))。具体的にはorg.eclipse.ui.model.BaseWorkbenchContentProviderとorg.eclipse.ui.model.WorkbenchLabelProvider を用いたビューワへのモデルの表示ですね。 #ref(EA46.png) このようなディレクトリ構造になっているモデルをBaseWorkbenchContentProviderとWorkbenchLabelProviderを使ってTreeViewerに表示したいと思います。トップレベルのクラスContactはPlatformObjectをextendsしています。 ***BaseWorkbenchContentProviderの実装がどうなってるか [#b1e4e77f] さて[[BaseWorkbenchContentProviderの実装:http://kickjava.com/src/org/eclipse/ui/model/BaseWorkbenchContentProvider.java.htm]]を調べてみました。たとえば public Object[] getChildren(Object element); をみてみると、elementオブジェクトがIAdaptableの場合、 IWorkbenchAdapter adapter = (IWorkbenchAdapter) ((IAdaptable) element).getAdapter(IWorkbenchAdapter.class); return adapter.getChildren(element); というように、castしてIWorkbenchAdapter#getChildren(Object element) を呼ぶ、という実装になってます。つまりモデルとなるelementがどのようなIWorkbenchAdapterを返すか、で挙動が決まるって事ですね。 今回はelementがContactクラス(の拡張クラス)なのでIAdaptableかつPlatformObjectですので、IWorkbenchAdapterを引数にgetAdapterしたときの返り値がどうなるかは、Eclipseプラットフォームに対して、Contact.classとともに登録されたIAdapterFactoryの実装クラスが、IWorkbenchAdapterを引数にgetAdapterしたときにどの様なオブジェクトを返すのか、が重要となります。 ***IAdapterFactoryの実装クラス [#cb0e9618] ここでは以下のように実装しました。 package org.eclipsercp.hyperbola.model; import org.eclipse.core.runtime.IAdapterFactory; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.model.IWorkbenchAdapter; public class HyperbolaAdapterFactory implements IAdapterFactory { ↓ ともにregistされたContact.classのgetAdapter(adapterType)がよばれたとき、呼び出される。 ↓ Object adaptableObjectはContactsGroupだったりContactsEntryだったり。 public Object getAdapter(Object adaptableObject, Class adapterType) { if (adapterType == IWorkbenchAdapter.class && adaptableObject instanceof ContactsGroup) return groupAdapter; if (adapterType == IWorkbenchAdapter.class && adaptableObject instanceof ContactsEntry) return entryAdapter; return null; } public Class[] getAdapterList() { return new Class[] { IWorkbenchAdapter.class }; } private IWorkbenchAdapter groupAdapter = new IWorkbenchAdapter() { public Object getParent(Object o) { return ((ContactsGroup) o).getParent(); } public Object[] getChildren(Object o) { return ((ContactsGroup) o).getEntries(); } public String getLabel(Object o) { ContactsGroup group = ((ContactsGroup) o); return group.getName(); } public ImageDescriptor getImageDescriptor(Object object) {return null;} }; private IWorkbenchAdapter entryAdapter = new IWorkbenchAdapter() { public Object getParent(Object o) { return ((ContactsEntry) o).getParent(); } public Object[] getChildren(Object o) { return new Object[0]; } public String getLabel(Object o) { ContactsEntry entry = ((ContactsEntry) o); return entry.getNickname() + " (" + entry.getName() + "@" + entry.getServer() + ")"; } public ImageDescriptor getImageDescriptor(Object object) {return null;} }; } ***ここまでのまとめ [#kd61337a] このファクトリを Platform.getAdapterManager().registerAdapters(adapterFactory,Contact.class); と Contact.classに登録することで、以下のことができたことになります。((たぶん)) -Contact.classやサブクラス(ContactsGroup、ContactsEntry)のgetAdapterが呼ばれたときは、このファクトリが起動して、getAdapter()が呼ばれます。 -このファクトリにより、Contact((と他のサブクラス))#getAdapter(IWorkbenchAdapter.class)に対しては、ContactsGroupならgroupAdapter、ContactsEntryならentryAdapter、Contactの場合はnull、なIWorkbenchAdapterオブジェクトを返却します。 なるほど、各IAdaptableなオブジェクトごとに書くよりかは、簡略化された、、、、かなあ???わかりにくいよねぇ。まだ理解できてないねえ。。 ちなみに、Viewを破棄するときにはunregisterするのを忘れないようにしましょう。 public void dispose() { super.dispose(); Platform.getAdapterManager().unregisterAdapters(adapterFactory); } ***関連リンク [#eda3e973] -[[What is IAdaptable?:http://www.eclipsezone.com/articles/what-is-iadaptable/]] ---- この記事は #vote(おもしろかった,そうでもない) -結局、対象となるオブジェクトがPlatformObjectとかIAdaptableとかを拡張してないとダメですね。誰かが作ったクラス、修正が不可能なクラスに対しての解がどっかにあるはず。。。org.eclipse.core.runtime.adapters拡張ポイントあたりだな、きっと -- [[きの]] &new{2006-03-27 14:24:03 (月)}; #comment #topicpath SIZE(10){現在のアクセス:&counter;}