さて、前回に引き続き、Eclipse Extension Point(拡張ポイント)の実装を行っていこう。
………え?前回が1年半前?
さぁて、何のことかな?
あ、今回から環境がJava10、Eclipse Photonになっております。
拡張ポイントを取得する
プラグイン開発において、拡張ポイントはIExtensionPointによって表現される。 各プラグインがplugin.xmlに記述する拡張ポイントの宣言は、IExtensionで表現される。
インターフェース | 対応するファイル | 取得方法 |
---|---|---|
IExtensionPoint | sample.core.books.exsd | IExtensionRegistry.getExtensionPoint |
IExtension | plugin.xml | IExtensionPoint.getExtension |
拡張ポイントは、IExtensionRegistryによって管理される。 IExtensionRegistryはPlatformから取得可能だ。
// import org.eclipse.core.runtime.IExtensionRegistry;
IExtensionRegistry registry=Platform.getExtensionRegistry();
拡張ポイントの取得はIExtensionRegistoryからgetExtensionPoint(String)により取得する。
// import org.eclipse.core.runtime.IExtensionPoint; IExtensionPoint point = registry.getExtensionPoint("sample.core.books");
拡張ポイントの宣言情報はIExtensionPointからgetExtensionで取得する。 拡張ポイントの宣言は拡張ポイントそのものと違って、複数回定義されうるものなので、配列で返される。
// import org.eclipse.core.runtime.IExtension;
IExtension[] extensions = point.getExtensions();
要素を処理する
IExtensionを取得出来たら、ようやくbook要素やauthor要素といった情報を解析する処理が始まる。 XMLを直接解析する必要はなく、DOMの様なデータ構造であるIConfigurationElementを処理する。
IExtenstion直下のIConfigurationElementを取得するには、getConfigurationElementsを使う。
// import org.eclipse.core.runtime.IConfigurationElement;
IConfigurationElement[] elements = extension.getConfigurationElements();
IConfigurationElementで扱うデータは、以下の物を理解しておけば十分だ。
データ | 取得方法 |
---|---|
子要素 | getChildren |
要素名 | getName |
属性 | getAttribute |
属性(kind = java) | createExecutableExtension |
名前空間 | getNamespaceIdentifier |
ただし、kindがjavaである属性の取得だけは注意が必要だ。
というのも、各バンドル(プラグイン)ごとにClassLoaderが異なる為、Class.forNameでクラスを見つけることが出来ないからだ。 createExecutableExtensionはその辺を解決して、インスタンスを生成してくれる。
(単にクラス名だけが必要なら普通にgetAttributeで取得しても良い。)
リソースの取得
前回の例で定義している、author.imageなど、リソースを取得する必要がある場合もある。
リソースは、拡張を定義した各プラグイン内のリソースを取得する必要がある為、Bundleを使って取得する必要がある。(Eclipseプラグイン開発: バンドルリソース関連を参照)
BundleはIExtensionやIConfigurationElementのgetNamespaceIdentifierで得られる名前空間名から取得可能だ。
Bundle bundle = Platform.getBundle(extension.getNamespaceIdentifier());
以下の様な関数を定義しておけば、利用する際に便利だろう。
public static URL findResource(Bundle bundle,String path){ return path == null || path.isEmpty()?null:FileLocator.find(bundle,new Path(path)); } public static ImageDescriptor findImage(Bundle bundle,String path){ return path == null || path.isEmpty()?null:AbstractUIPlugin.imageDescriptorFromPlugin(bundle.getSymbolicName(),path); }
Expression
前回、最後に導入したenablementの要素はorg.eclipse.core.expressionsパッケージが処理を行う。
処理の流れを以下に示す。
- IConfigurationElementをExpressionに変換
- ExpressionContextを用意し、式を評価(evaluate)し、ExpressionResultを取得
- 扱いやすい様にbooleanに変換
2の処理を行うタイミングは、式の内容による。 一度計算すれば二度と変化しないなら、最初に計算してしまえば良い。 一方で、設定などに依存する場合はその都度計算する必要があるだろう。
boolean enable = true; IConfigurationElement[] enablement=book.getChildren("enablement"); if(enablement.length==1){ Expression exp= ExpressionConverter.getDefault().perform(enablement[0]); EvaluationContext ctx = new EvaluationContext(null,""); ctx.addVariable("hoge","HOGE"); // hogeという変数を定義 EvaluationResult r = exp.evaluate(ctx); enable = r == EvaluationResult.TRUE; }
拡張ポイントを処理するタイミング
拡張を定義するplugin.xmlは起動時に読み込まれる為、基本的に変化しない。 従って、Bundleがアクティブになった後、必要になった時に1回だけ読み込む様にするのが良いだろう。
(無論、メモリを圧迫するなどの理由で毎回読み込んでも良いが)
一応、Bundleが破棄されたら、それらの情報も破棄した方が良い。
拡張ポイントの読み込みサンプルプログラム
Github:extension_point_sampleに前回定義したcore.sample.booksを読み込むサンプルプログラムを用意した。
このプログラムはsample.internal.core.Extensionsクラスで拡張ポイントを処理してBookクラスにコンバートする。 コンバートした結果はActivatorのstartで取りこまれ、stopで破棄される。