ここ最近ではあちこちで「動的型付け」に関する
話題が取り上げられることが多いように感じます。
以前にも触れたようにGroovyなどの動的型付けが
手軽に実現できる言語は今後増えると思いますし
Javaについても次期のDolphinでは動的な要素も
加わるらしいです。
と、そこでEclipseにおいてもある種の「動的型付け」を
試みているのでは?と思われる仕組みがあったのに
気づきました。
Eclipseではplugin.xmlにExtensionを記述することで
実に様々なビューやアクションを付け加えることが可能
なわけですが、Package Explorerのように異なる種類
のエレメントを表示するビューの中で、表示されている
全要素に対してポップメニューを追加したいといった
場合には面倒なことになります。
一般のファイルやフォルダはorg.eclipse.resouces.IFileや
org.eclipse.resources.IFolderといったインターフェース
を実装していますが、Javaのソースファイルやパッケージは
それぞれ、org.eclipse.jdt.core.ICompilationUnitや
org.eclipse.jdt.core.IPackageFragmentを実装しています。
ポップアップメニューの追加ではContributeするオブジェクト
(つまり、ポップアップメニューが表示される対象となるもの)
を明示的に指定する必要があるため、普通に記述したのでは
plugin.xmlは下記のような感じになってしまいます。
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.0"?> <plugin ...> <runtime>....</runtime> <requires>...</requires> <extension point="org.eclipse.ui.popupMenus"> <objectContribution id="jp.co.linkcom.MyPlugin.FilePopupContribute" objectClass="org.eclipse.core.resources.IFile" ---(A) objectClass="org.eclipse.jdt.core.ICompilationUnit"> ---(B) <action label="PopUp" id="jp.co.linkcom.MyPlugin.PopUpAction" class="jp.co.linkcom.MyPlugin.PopUpAction" menubarPath="DefaultGroup" enablesFor="1"> </action> </objectContribution> </extension> </plugin>
こうすると(A)や(B)にあるように対象の型を個別に記述しなければ
ならないので、あまり綺麗ではありません。これを解決する手段として
EclipseではIAdaptableというインターフェースを定義しています。
このIAdaptableインターフェースですが、getAdapter(Object obj)
というメソッドが定義されているだけです。ですが、非常にたくさんの
クラスやインターフェースに実装/継承されています。
このgetAdapter(Object obj)の役割ですが、「あるオブジェクトが
そのオブジェクトを使う側にとって望ましいと思うインタフェースを
提示したときに、そのインターフェースやそのサブインタフェースを
オブジェクトがサポートしていればそれを返し、そうでなければnull
を返す」という動きをするようです。
言葉で書くと何だか良くわからないので、実例を書いてみることにします。
先のorg.eclipse.jdt.core.ICompilationUnitはIAdaptableを実装しています。
それを踏まえると、先の例は以下のように書きなおせます。
<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.0"?> <plugin .....> <runtime>....</runtime> <requires>...</requires> <extension point="org.eclipse.ui.popupMenus"> <objectContribution id="jp.co.linkcom.MyPlugin.FilePopupContribute" objectClass="org.eclipse.core.resources.IFile" ---(C) adaptable="true"> <action label="PopUp" id="jp.co.linkcom.MyPlugin.PopUpAction" class="jp.co.linkcom.MyPlugin.PopUpAction" menubarPath="DefaultGroup" enablesFor="1"> </action> </objectContribution> </extension> </plugin>
対象はorg.eclipse.core.resources.IFileだけにする代わりに
adaptableというオプション属性を追加します。これによって、
「ICompilationUnitがIFileとしての動作もサポートするかどうか
をチェックする」ということを指示していることになります。
そして実際にJDTの実装ではサポートされているので、
上記の方法でうまく動作させることができます。
アクションを処理するjp.co.linkcom.MyPlugin.PopUpAction側では
どのように記述すれば良いかというと以下のような感じになります。
つまり、アクション元のオブジェクトをIAdaptableインターフェースにキャストし
public class PopUpAction implements IWorkbenchWindowActionDelegate {public void dispose() {...}
public void init(IWorkbenchWindow window) {...}
public void run(IAction action) {...}
public void selectionChanged(IAction action, ISelection selection) {
IStructuredSelection selections = (IStructuredSelection)selection;
IAdaptable adaptable = (IAdaptable)selections.getFirstElement();
IFile file = (IFile)adaptable.getAdapter(IResource.class);
......
}}
getAdapter(Object obj)を呼び出すことで欲しいインタフェースを取得するという
流れになります。ここではIFileやIFolderの親にあたるIResourceインターフェース
を指定しています。
このようにIAdaptableインターフェースを共通基盤とすることによって、種々雑多な
オブジェクトが混在するような状況でも極力設定やコーディングがシンプルになるよう
配慮されているということができるかと思います。「動的型付け」という観点で言えば
上記のplugin.xmlの例のように個々のオブジェクトが望ましい動きを備えているか?
ということをプラットフォーム側である程度自動的に行なってくれる点が「動的」と
言えるのではないかと思います。ちょっと違うかも知れませんが、コンセプト的には
COMのIUnknownインターフェースに近い発想なのかも知れませんね。
ただ、このアプローチには大きな問題があります。リファレンスなどをパッと見ても
org.eclipse.jdt.core.ICompilationUnitがorg.eclipse.core.resources.IFile
としても振舞うことができるということはなかなか読み取れなかったりします。
(ボクが見つけられないだけかも知れませんが....)インターフェースを明示的に継承
させているのとは異なるので、リフレクションでも見つけられないかも知れません。
当面は「異なるオブジェクトを統一的に扱いたい場合は、対象オブジェクトの
getAdapter(Object obj)を呼び出してみて、望ましい共通インタフェースが
ないかどうかをチェックする」というのが現実的なようです。かなり根本的な
部分なので劇的な改善は難しいかも知れませんが、「各オブジェクトがどんな
なインタフェースを返すことができるのか?」ということをもっと手軽に把握
できる仕組みができたらいいなと思います。(もし、何かいいワザをお持ちの
方がいらっしゃいましたら教えてください^^)