プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

Eclipseプラグイン開発: 拡張ポイントの定義

Eclipse プラグイン開発 目次



 Eclipseプラグイン開発でほぼ確実に避けて通れないのが拡張ポイント。拡張ポイントはなんだかよく分からんけど、裏でEclipseが上手いこと処理して、何か素晴らしぃことをしてくれる仕組み………なんてふつくしぃ世界はなく、泥臭い処理がEclipseプラグイン開発者の手によって走らされております。

 拡張ポイントとは何なのか?それを理解するには、まず自分で一回作ってみるのが、何よりも一番手っ取り早くて確実です。他人が用意した拡張ポイントを使う前に、実装してみることをお勧めします。




拡張ポイントとは

 XMLとXML Schemaである。以上。解散。


 一切の冗談抜きにこれだけです。XMLは説明不要でしょう。HTMLによく似た奴です。XML SchemaはXMLに書ける要素(HTMLならdivとかaとかimgとか)を定義するファイルです。

 XMLとXML Schemaを手で書くのは流石にしんどいので、手打ちせずにGUIで設定できるエディタが提供されてるだけです。後は、Eclipse起動時にXMLを勝手に読み込んで、DOM(みたいなもの)にしてくれるぐらいです。

 じゃぁ、どうして拡張ポイントを他のプラグイン開発者が利用しただけで、色々な機能を拡張できるのでしょうか?それは当然、拡張ポイントを定義した人はシコシコXML解析するプログラムを作って、シコシコ解析した情報を自分のプラグインにゴリゴリ統合しているからです。

 決して謎の技術ではなく、裏でそういう血と涙と汗が流れている技術なのです。


 

拡張ポイントを定義する

 拡張ポイントをシコシコ解析する前に、シコシコ解析されるXMLを書くための定義を書かなくてはなりません。

 ここでは、例として「本」を定義する拡張ポイント「sample.core.books」を作ってみましょう。

 拡張ポイント(Extension Point)タブの追加から新規拡張ポイントを定義できます。

日本語化した場合:
f:id:nodamushi:20170414005600p:plain

英語の場合:
f:id:nodamushi:20181007014848p:plain
 

 今回の例では、次のような情報を持たせようと思います。

  1. 本はタイトルを持つ
  2. 本はユニークなISBNを持つ
  3. 本はカテゴリを持つ
  4. 本は一人以上の複数の著者からなる
  5. 本は1P以上のページからなる
  6. 本は複数の他の本への参照を持つ

 

要素の定義をする

 HTMLでは、<body><a><span><table><img>など、多くの要素を利用することが出来ます。HTMLの仕様によってこれらがHTMLの要素と定義されているからです。

 同じように、拡張ポイントで使用することができる要素を定義していきましょう。

 新規に拡張ポイントを定義すると、最初はextension要素しか定義されていません。このextension要素は、HTMLで言えば<html>要素と同じで、拡張ポイントのルート要素です。削除はできません。

 新規要素ボタンを押して、要素bookを作ってみましょう。

f:id:nodamushi:20170414005629p:plain

英語の場合(以下英語は略):
f:id:nodamushi:20181007015015p:plain


f:id:nodamushi:20170414005640p:plain

 同様に、author、page、reference要素を宣言しておきました。

f:id:nodamushi:20170414005651p:plain

 

属性の定義をする

 各要素は属性を持つことが出来ます。HTMLで言えば、<img src="image.png" alt="画像です" id ="myimg">とあった場合、srcやalt、idが属性です。

 属性にはuseとtypeを指定することが出来ます。

 

use

 例えば、imgタグのsrc属性が無かったら画像を表示することが出来ません。HTMLは解釈がゆるいのでなくても構いませんが、出来ることならユーザーに入力を強制したいものです。その指定をuseで決めます。

use 説明
require 必須で、必ず何らかの入力を要求する。
optional 値を書いても書かなくても許される。
default 初期値が最初から入力される。

 

type

 imgタグのsrc属性はファイルの場所を示す「文字列」、alt属性は画像が読めなかったときに表示する「文字列」といった風にそれぞれ入力する内容が決まっています。typeではどういった「文字列」を入力するのかの指定をします。

type 説明
string 文字列を入力させる。制限事項を使うことで、入力値をコンボボックスで選べる
boolean trueかfalseかのどちらかだけを指定できる文字列
java 特定のクラスのサブクラス、または特定のインターフェースの実装クラスだけを指定できる文字列
resource プラグイン内のリソースのパスを入力する支援ボタンがついた文字列
identifier 拡張ポイントの特定の要素の特定の要素と同じ値を入力する支援ボタンがついた文字列

 

 

 では、book、author、page、referenceに属性を定義してみましょう。

 bookには、title属性、isbn属性、category属性を設定しました。category属性は選択制で、デフォルトはとりあえず工口本にしておきました。

f:id:nodamushi:20170414005702p:plain

 拡張ポイントを使う側のエディタでは、bookの属性入力画面がこんな感じになります。

f:id:nodamushi:20170414005709p:plain

 

 authorにはname属性と、顔写真を載せるためにimage属性を追加しました。

f:id:nodamushi:20170414005717p:plain

 同様に、authorの属性入力画面はこんな感じです。imageにリソース選択ボタンが追加されます。

f:id:nodamushi:20170414005723p:plain

 

 pageにはnumber属性と、ページの内容を表すcontents属性を追加しました。contentsはダイナミックなコンテンツかも知れないので、プログラマブルに指定できるように、sample.core.IBookPageを実装したクラスとしました。

f:id:nodamushi:20170414005730p:plain

 入力画面では、contents属性をクリックすると、IBookPageを継承するクラスを作ることが出来たり、既に存在するIBookPageクラスを選択できるボタンが追加されます。

f:id:nodamushi:20170414005742p:plain

 

 referenceには、参照したい本のISBNを指定させることにします。

f:id:nodamushi:20170414005748p:plain

 入力画面では、既に定義されているbook要素のisbmの値を選べるボタンが追加されます。

f:id:nodamushi:20170414005754p:plain



要素の子要素を定義する

<table>
  <tr><td/></tr>
  <tr><td/></tr>
</table>

 上のHTMLのように、table要素の下にはtr要素を、tr要素の下にはtd要素を配置することが出来ます。

 拡張ポイントの要素も同様に子要素を持つように指定することが可能です。

choice (選択)

 正規表現的に書けば(A|B|C)になります。各要素は個数指定できるので、大ざっぱに書くと、(A*|B*|C*)*ですかね。要素A,B,Cが何個出てきても構わない、というときにchoiceを使うとよいです。

 

sequence (シーケンス)

 正規表現的に書けば(ABC)になります。各要素は個数指定できるので、大ざっぱに書けば、(A*B*C*)*ですかね。なお、完全に正規表現ではないので、AB*C*という指定にした場合も、ABCBCという入力を受け付けます。

 例えばtitle要素は必ず一つ、description要素は0または1、その他の要素は何個でもという様な場合はsequenceを使います。



 さぁ、それでは「sample.core.books」拡張ポイントを完成させてしまいましょう。拡張ポイント直下には「book」を1つ以上できるので、choiceを使って(book)+としました。なお、(book+)でも構わないっちゃ構わない。

 次に、bookには一人以上の著者、1P以上のページ、0個以上の他の本への参照を持つので、sequenceを用いて、(author)+(page)+(reference)*としました。(下図の左)

 エディタでは、下図の右のように要素を構成できます。

f:id:nodamushi:20170414005809p:plain



他の拡張ポイント定義を利用する


 よくスキーマを見ると、「スキーマの取り込み」という項目があります。スキーマは他のスキーマをインクルードすることが出来ます。

f:id:nodamushi:20170414005818p:plain

 

 インクルードすることで、他のファイルで定義した要素をそのまま再利用することが可能になります。

 といっても、この項目をそのまま使うことはまず無いでしょう。そんなに再利用したくなるほど、すゥンばらしィ定義ファイルを自分で作るなんて、普通ないでしょうし。でも、この追加ボタンで追加できるのは、自分で定義したファイルだけなのです。


 それではあまり意味がないので、折角なので、知っておくと非常に有用な要素を取り込んでみましょう。
 ある場面では、拡張を有効化したいけど、ある場面では無効化したいという場面は多いですよね。これは「org.eclipse.core.expressions.definitions」を別途定義してもらい、definitionsに指定したIDを受け取ることで実現可能です。しかし、定義を二個に分離するのも面倒ですね。definitionsで書く内容を我々の拡張ポイントに取り込んでみましょう。(取り込んだものを、Javaで実際にどうやって使うのかは次回説明します)

 まずは、必須プラグインに「org.eclipse.core.expressions」を追加します。

 次に、スキーマのエディタで、「ソース」を選択し、XMLの生ソースを表示します。

annotationの後ろあたりで良いので、次のincludeを追加して下さい。

<include schemaLocation=
  "schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/>


 全体としてはこんな感じの場所です。schema直下だったらどこでも良いと思うけど。

f:id:nodamushi:20170414005827p:plain


 では、定義の画面に戻ってみましょう。bookのシーケンスを右クリックして、新規を選択すると、book,page,author,reference以外のadaptなど今回定義していない要素がずらっと並びます。

f:id:nodamushi:20170414005835p:plain


 この中からenablememtを選んで追加して下さい。


 さて、これで自分独自の拡張ポイントを定義することに成功しました。しかし、まだ定義しただけです。これを自分で解析する機構を作らなくてはなりません。

 というわけで、次回に続く。