EMF モデルのコンテンツをナビゲートするための Eclipse プラグインを作成する

Common Navigator Framework と Eclipse Modeling Framework を使って、EMF モデルのコンテンツを操作、ナビゲートする

EMF.Edit と CNF (Common Navigator Framework) を利用してツリー・ビューアーをベースとしたモデル・ナビゲーション・プラグインを作成する方法を学んでください。この記事では、ユーザーが EMF (Eclipse Modeling Framework) モデルのコンテンツを操作、ナビゲートできるようにする Eclipse プラグインを作成します。プラグインを開発し、EMF 編集フレームワークでモデルのコンテンツを抽出するのに適切な構造を実装して、CNF ベースのビュー・パーツにコンテンツを表示する方法をステップバイステップで解説します。

Javier Torres (jrtorres@us.ibm.com), Staff Software Engineer, Sunnyside Avenue Software, LLC

Javier Torres は、IBM Software Group の新たな標準組織に所属するソフトウェア・エンジニアです。IBM に入社したのは 2007年で、現在はエネルギーおよびユーティリティー産業向け標準ベースのソリューション開発を中心に活躍しています。彼は Florida International University でコンピューター・サイエンスの修士号、そしてコンピューター・エンジニアリングの学士号を取得しました。



2007年 9月 04日

一般的に、EMF プロジェクトのリソース (例えば EMF Ecore モデル) は単一のオブジェクトとしてビューアーに表示されます (図 1 の左側を参照)。この場合、モデルを検討するにはそのモデルに関連付けられたエディターを開かなければならないため、ドメイン・モデルに依存した開発では特に、厄介な制約となる可能性があります。この制約を回避する方法は、カスタム・ビューを作成し、目的とするドメイン・モデルのコンテンツにアクセスできるようにすることです。このプラグインは一から作成することも、既存のフレームワークを使って開発作業を軽減することもできます。これから概要と詳細を説明するのは、このようなプラグインを作成する手順です。この記事を読み終える頃には、Ecore モデルのナビゲーションに利用できるビューアー・プラグインが完成しているはずです (図 1 の右側を参照)。

図 1. ナビゲーターのタイプ
ナビゲーターのタイプ

背景

この ModelNavigator プラグインに求める内容は明らかですが、開発を始める前に、これから使用する Eclipse コンポーネントの基本を理解しておかなければなりません。ここで作成するのは、モデルの階層を表示するツリー・ビューアーです。ツリー・ビューアーについての詳細はこの記事の範囲外なので、「参考文献」を参照してください。注意すべき重要な点は、ツリー・ビューアーはコンテンツ・プロバイダーと呼ばれるアダプターを介してモデル・オブジェクトにアクセスし、ラベル・プロバイダーを介してオブジェクトの表示方法を決定するということです。以降のセクションではコンテンツ・プロバイダーおよびラベル・プロバイダーを介した場合のデータ・アクセス方法、そしてモデル・ナビゲーターの表示方法を詳しく説明します。

EMF.Edit フレームワーク

EMF モデルを対象としたエディターを作成するために通常使用されるのは、EMF.Edit フレームワークです。EMF.Edit フレームワークは EMF モデル用のエディターを作成するためのコマンド・コードの生成機能やその他のクラスを提供し、プログラムによるモデルへのアクセスを可能にします。それとは別にこのフレームワークが提供している (そしてこの記事ではとりわけ重要な) 一連のフィーチャーが、EMF モデルをビューアーに表示できるようにする便利なクラスです。これらのクラスへのアクセスに使用される汎用のコンテンツ・プロバイダーとラベル・プロバイダーが、特定タイプの EMF オブジェクトに対応したアダプターを使ってモデルを表示します。コンテンツ・プロバイダーとラベル・プロバイダーに該当するのは、それぞれ AdapterFactoryContentProvider クラスと AdapterFactoryLabelProvider クラスです。この 2 つのクラスが EMF モデルのナビゲート方法を認識するアイテム・プロバイダー・アダプターに委任して、EMF オブジェクトのビューアーにオブジェクト、ラベル、そして画像を提供するというわけです。この図式を図 2 に示します。このような図式が私たちのプロジェクトにとりわけ有用である理由は、モデルをビューに適応させる方法を理解する必要がなくなるからです。モデルのビューへの適応は、コンテンツ・プロバイダーとラベル・プロバイダーに任せてしまうのです。

図 2. Eclipse ヘルプから引用した EMF.Edit
Eclipse ヘルプから引用した EMF.Edit

フレームワークにはそれぞれの EMF モデル・タイプに対応したアイテム・プロバイダーが含まれますが、たいていの場合はモデル・タイプごとに別個のナビゲーターを作成するのではなく、同じ 1 つのナビゲーターからすべてのモデルのコンテンツにアクセスしたいと思うものです。そこで登場するのが複合アダプター・ファクトリーです。複数のモデルからのオブジェクトの集合に適応可能な複合アダプター・ファクトリーを提供することで、さまざまな EMF モデルのオブジェクトを同時に表示できるようになります。

EMF.Edit には、他のアダプター・ファクトリーへの共通インターフェースとして機能する ComposedAdapterFactory という便利なクラスもあります。このクラスの利点は後で説明するように、ナビゲーターには対象のモデル・タイプ用のアイテム・プロバイダーを複合アダプター・ファクトリーの一部として組み込むだけで済むことです。そうすると、後は複合アダプター・ファクトリーがその一部となっている他のプロバイダーに実装を委任してくれます。この記事のサンプル・ナビゲーターでも、例えばジェネレーター・モデル、Ecore モデル、UML モデルなどを表示するようにできます。ビューアーがこれらのモデルを表示しようとすると、コンテンツ・プロバイダーとラベル・プロバイダーがアダプター・ファクトリーに委任し、委任されたアダプター・ファクトリーが該当するアイテム・プロバイダーに委任するという仕組みです。これによって、開発作業が楽になると同時に、ModelNavigator プラグインを拡張して複数のドメイン・モデルをサポートさせることも可能になります。

Common Navigator Framework

Common Navigator Framework の詳細

Michael Elder のブログには、CNF (Common Navigator Framework) の機能、使用方法に関するチュートリアルが豊富に揃っています (「参考文献」を参照)。CNF に興味のある開発者は是非、これらのチュートリアルを読んでください。

フレームワーク全体が準備できて ModelNavigator プラグインでモデルのコンテンツにアクセスできるようになったら、次に必要となるコンポーネントはこのナビゲーターを収容する実際のビューアーです。ビュー・プラグインを拡張して、目的のコンテンツをツリー・ビューで表示する独自のビュー部分を実装するという方法もありますが、さまざまなドメインのコンテンツを単一のビューに結合してユーザーがビューアーでエディター・モデルを操作、ナビゲートできるようにする既存のフレームワークもあります。それが、Eclipse V3.2 に CNF として導入された org.eclipse.ui.navigator です。開発者は CNF を使用して、コンテンツ、ラベル、アクション、フィルターなどの機能を単一のナビゲーターに提供することができます。CNF は、複数のナビゲーション用ビューアーを統合して統一したユーザー・エクスペリエンスを実現する手段となります。

すべてのエディター・モデル・インテグレーターに対応した単一のビューをサポートする CNF では、リソースに左右されないモデルのコンテンツが可能になるとともに、統合されたビューアーの表示内容をユーザーが選択することができます。このフレームワークの実際の例が Project Explorer ビューという形で表示される org.eclipse.ui.navigator.resources プラグインで、IResource モデルに宣言型のビューアー拡張を提供します。ビューアーを作成するには、このフレームワークを使うのが最も簡単な方法です。ビューアー実装の詳細はフレームワークが処理してくれるので、私たちに必要なのはモデルのコンテンツ・プロバイダーとラベル・プロバイダーを実装する作業だけとなります。さらに、プラグインを他のモデルのコンテンツに応じて拡張する可能性が与えられるだけでなく、より高度なフレームワークの機能 (ソーター、フィルター、ドラッグ・アンド・ドロップなど) をビューアーで利用できるようにもなります。

以上の説明で実際のコンポーネントに目を向ける準備が整ったので、いよいよモデル・ナビゲーション・プラグインの作成に取り掛かります。


単純なナビゲーター

ModelNavigator プラグインの作成は 2 段階で行います。第一段階は、プラグインの基本構造をセットアップしてその動作を定義します。続く第二段階で、この単純なプラグインを拡張して EMF モデルのコンテンツを表示させます。

ModelNavigator プロジェクト

ModelNavigator プラグインを作成するための最初のステップとして、プラグイン・プロジェクトを作成します。それには新規プロジェクト・ウィザードを使うので、File > New > Project から Plug-in Project を選択し、Next をクリックします。

プラグイン・プロジェクトの名前として ModelNavigator と入力し、プラグイン ID をcom.ibm.navigator.example.ModelNavigator に、アクティベーターを com.ibm.navigator.example.ModelNavigator.ModelNavigatorPlugin に設定して Finish をクリックします。

図 3. プラグイン・プロジェクトの作成
プラグイン・プロジェクトの作成

ビューを作成する

これで、プラグインの開発に取り掛かる準備ができました。プラグイン開発の最初のステップは、ナビゲーターを収容するビューを作成することです。ビューを作成するには、プラグインを拡張してビュー部分およびビューを表示するカテゴリーを作成します。プラグインの xml ファイルには、ビュー拡張を追加します。

以下の作業は、Extensions タブから行います。

  • Add をクリックします。
  • Extension points タブで org.eclipse.ui.views を選択します。
  • 以下の手順でカテゴリーを作成します。
    • org.eclipse.ui.views 拡張を右クリックします。
    • New > Category を選択します。
    • 名前を com.ibm.navigator.example.ModelNavigator.mncategory に設定します。
    • ID を Model Navigator に設定します。
  • 以下の手順でビュー部分を作成します。
    • org.eclipse.ui.views 拡張を右クリックします。
    • New > View を選択します。
    • ID を com.ibm.navigator.example.ModelNavigator.mnview に設定します。
    • 名前を Model Navigator View に設定します。
    • クラスを org.eclipse.ui.navigator.CommonNavigator に設定します。
    • カテゴリーを com.ibm.navigator.example.ModelNavigator.mncategory に設定します。
  • 変更内容をプロジェクトに保存します。

前述したように、実装作業の一部は CNF に任せています。このビューのセットアップ方法に関して注意すべき重要な点は、ビューの ViewPart を継承する独自のクラスを作成する代わりに、ビューが共通ナビゲーター・クラスを使用するように設定していることです。これはつまり、クラス・フィールドで指定する共通ナビゲーター・クラスをプラグインのクラス・パスに追加しなければならないということでもあります。したがって、Dependencies タブで必要なプラグインに org.eclipse.ui.navigator を追加してください。

デフォルト動作を設定して CNF に組み込む

次のステップは、プラグインの CNF 属性を構成します。これらの属性によって、ナビゲーターのデフォルト動作が決まります。

まず、ビューを共通ナビゲーター・ビューアーに関連付けます。それには、Extensions タブから以下の作業を行います。

  • Add をクリックします。
  • Extension points タブで、org.eclipse.ui.navigator.viewer を選択して Finish をクリックします。
  • 以下の手順に従ってビューを作成します。
    • org.eclipse.ui.navigator.viewer 拡張を右クリックします。
    • New > viewer を選択します。
    • viewerid を上記で定義したビュー ID (com.ibm.navigator.example.ModelNavigator.mnview) に設定します。

ナビゲーター・プラグインの動作は、バインドするコンテンツとアクション、そして CNF を使用するときにプラグインに設定するその他のオプション (フィルター、ソーターなど) によって決まります。このプラグインで、ビューアーに表示する拡張を選択するのは includes 要素です。フレームワークに対しては、コンテンツ拡張がビューでのコンテンツの表示方法を指示し、アクション拡張がビューに有効なオプション (コンテキスト・メニューなど) を指示します。この必要最小限のナビゲーター・バージョンを稼動させるため、IResource モデルのコンテンツおよびアクション・バインディングを追加し、プラグインにお馴染みの Package Explorer のフィールを与えます。そのために作成するのが、ナビゲーターにリソース・コンテンツ拡張を追加する viewerContentBinding、そしてリソース・アクション拡張を追加する viewerActionBinding です。

IResource コンテンツ拡張を追加するには、Extensions タブで以下の手順に従います。

  • org.eclipse.ui.navigator.viewer 拡張を右クリックします。
  • New > viewerContentBinding を選択します。
  • viewerid をプラグインの viewpart ID (com.ibm.navigator.example.ModelNavigator.mnview) に設定します。
  • 以下の手順でコンテンツ・バインディングを追加します。
    • 上記の手順で作成した viewerContentBinding を右クリックします。
    • New > includes を選択します。
    • 作成した includes 要素を右クリックします。
    • New > contentExtension を選択します。
    • パターンを org.eclipse.ui.navigator.resourceContent に設定します。

IResource アクション拡張をバインドするには、Extensions タブで以下の作業を行います。

  • org.eclipse.ui.navigator.viewer 拡張を右クリックします。
  • New > viewerActionBinding を選択します。
  • viewerid をプラグインの viewpart ID (com.ibm.navigator.example.ModelNavigator.mnview) に設定します。
  • 以下の手順でアクション・バインディングを追加します。
    • 作成した viewerContentBinding を右クリックします。
    • New > includes を選択します。
    • 作成した includes 要素を右クリックします。
    • New > actionExtension を選択します。
    • パターンを org.eclipse.ui.navigator.resources.* に設定します。

ナビゲーターの構造はこれでセットアップできました。ナビゲーター・フレームワークにどのタイプのコンテンツを表示するか、そしてナビゲーターをどのように動作させるかを指示する方法は以上のとおりです。これらの拡張は必ずしも追加しなければならないわけではありませんが、IResource モデルのコンテンツとアクションを追加すると一層有用なビューになります。コンテンツをビューアーにバインドする方法がわかれば、ビューアーをさらに拡張してカスタム・フィルターや定義済みフィルターを追加することも可能です。これらの機能についての詳細は、「参考文献」を参照してください。このナビゲーターはまだ完成したわけではありません。ビューアーに EMF モデルを表示できるようにする必要がまだ残っています。


EMF ドメイン・コンテンツの追加

EMF ドメイン・モデルのコンテンツをナビゲーターに表示するために、フレームワークに伝えなければならないことは、どのモデルを対象とするのか、この対象モデルからどのようにして情報を取得するのか、そしていつ、どのように情報を表示するのかといったことです。そのために作成するのが、コンテンツ拡張です。ナビゲーター・コンテンツ・サービスがこの拡張を使って EMF ドメイン・モデルを表示できるようにします。この目的を果たすには、org.eclipse.ui.navigator.navigatorContent プラグインの拡張を作成します。

ナビゲーター・コンテンツ拡張が定義するのは、EMF モデルの要素に子および親オブジェクトを提供する際に使用できるコンテンツ・プロバイダーとラベル・プロバイダーです。ナビゲーター・コンテンツ拡張は、この triggerPoints として知られる子オブジェクト、または possibleChildren として知られる親オブジェクトを提供するためにこの拡張を呼び出す条件も定義します。また、ナビゲーター・コンテンツにはその他多くの属性があり (アクション・プロバイダー、共通ウィザード、フィルターなど)、これらの属性を変更することでプラグインの動作を変更することもできます。ただし、これらの属性は追加機能なので、この ModelNavigator プラグインの作成には必要ありません。

まず始めに新しいコンテンツ拡張を作成し、そこに共通ナビゲーターが使用するコンテンツ・プロバイダーとラベル・プロバイダーを指定します。その後、この 2 つのクラスを実装することになります。

navigatorContent 拡張を追加するには、Extensions タブから以下の作業を行います。

  • Add をクリックします。
  • Extension points タブで、org.eclipse.ui.navigator.navigatorContent を選択して Finish をクリックします。
  • 以下の手順でナビゲーター・コンテンツを作成します。
    • org.eclipse.ui.navigator.navigatorContent 拡張を右クリックします。
    • New > navigatorContent を選択します。
    • ID を com.ibm.navigator.example.ModelNavigator.emfModelContent に設定します。
    • 名前を EMF Model Content に設定します。
    • コンテンツ・プロバイダーを com.ibm.navigator.example.ModelNavigator.MNViewContentProvider に設定します。
    • ラベル・プロバイダーを com.ibm.navigator.example.ModelNavigator.MNViewLabelProvider に設定します。
    • 優先度を normal に設定します。
    • activeByDefault を true に設定します。

次に、このナビゲーター・コンテンツのコンテンツ・プロバイダーおよびラベル・プロバイダーとして機能するクラスを実装します。

複合アダプター・ファクトリー

コンテンツ・プロバイダーとラベル・プロバイダーの実際の作業を実行するには、EMF.Edit フレームワークを使用します。したがって、コンテンツ・プロバイダー・クラスとラベル・プロバイダー・クラスは EMF 編集フレームワークのアダプター・ファクトリーに含まれるサブクラスとして作成しなければなりません。AdapterFactoryContentProviderAdapterFactoryLabelProvider にそれぞれ委任するコンテンツ・プロバイダー・クラスおよびラベル・プロバイダー・クラスを作成する前に念頭に置いておかなければならないのは、これらのアダプター・ファクトリーは各種 EMF モデルのアイテム・プロバイダーのリストを使ってインスタンス化されるということです。つまり、まずはアイテム・プロバイダーのリストを複合アダプター・ファクトリーとして作成する必要があるので、MNComposedAdapterFactory という名前の新規クラスを作成します (File > New > Class)。

図 4. 実装クラスの作成
実装クラスの作成

さらに、Dependencies タブで必要なプラグインに org.eclipse.emf.codegen.ecore.ui を追加する必要があります。こうすることによって、プラグインがさまざまな EMF モデル・タイプの ComposedAdapterFactory アイテム・プロバイダーやその他の EMF.Edit のコンビニエンス・クラスを使用できるようになります。MNComposedAdapterFactory クラスの実装はリスト 1 のようになるはずです。

リスト 1. サンプル ComposedAdapterFactory クラス
...
public class MNComposedAdapterFactory
{
    private static ComposedAdapterFactory mnCompAdapterFactory;

    public final static ComposedAdapterFactory getAdapterFactory()
    {
        if (mnCompAdapterFactory == null)
            mnCompAdapterFactory = new ComposedAdapterFactory(createFactoryList());
        return mnCompAdapterFactory;
    }

    public final static ArrayList<AdapterFactory> createFactoryList()
    {
        ArrayList<AdapterFactory> factories = new ArrayList<AdapterFactory>();
        factories.add(new ResourceItemProviderAdapterFactory());
        factories.add(new EcoreItemProviderAdapterFactory());
        factories.add(new ReflectiveItemProviderAdapterFactory());
        return factories;
    }
}

上記で注目すべき重要な箇所は、アイテム・プロバイダーのリストを作成する静的メソッドを作成しているところです。リストされているアイテム・プロバイダーのうち、アダプター・ファクトリーが Ecore モデルのコンテンツとラベルを提供するために委任するのは EcoreItemProviderAdapterFactory となります。

コンテンツ・プロバイダーとラベル・プロバイダー

同じようにして、navigatorContent 拡張詳細で指定したコンテンツ・プロバイダー・クラスとラベル・プロバイダー・クラスを作成します。

まずはコンテンツ・プロバイダー (MNViewContentProvider) から取り掛かります。このクラスを作成するには、拡張詳細パネルに表示されたプロバイダー名の隣にあるリンクをクリックするか、File > New > Class ダイアログに進みます。コンテンツ・プロバイダーの ITreeContentProvider インターフェースを実装する代わりに、ここでは EMF.Edit クラスを使用します。それには MNViewContentProvider クラスを変更して AdapterFactoryContentProvider クラスを継承します。AdapterFactoryContentProvider クラスは、ビューアーにコンテンツを提供する ITreeContentProvider インターフェース実装の処理方法をすでに認識しているので、実際には該当するアイテム・プロバイダーに委任してビューアーにコンテンツを提供させます。

AdapterFactoryContentProvider クラスは、コンストラクターにアダプター・ファクトリーを含んでいることが前提となります。つまり、コンテンツ・プロバイダーには暗黙のコンストラクターは使用できないということです。コンテンツ・プロバイダーのコンストラクターは明示的に宣言し、必要なパラメーターを指定してスーパークラス・コンストラクターを呼び出さなければなりません。宣言したコンストラクターには super(MNComposedAdapterFactory.getAdapterFactory()); を追加します。その他にこのクラスで注目すべきメソッドには、getChildrengetParenthasChildrengetElements があります。

getChildren メソッドは、ビューアーがドメイン・オブジェクトの子要素を表示しなければならないときに呼び出されます。するとこのメソッドは、パラメーター要素の子であるドメイン・オブジェクトの配列を返します。同様に、getElements メソッドはパラメーター要素のドメイン・オブジェクトを取得するために呼び出されます。同じように動作するこの 2 つのメソッドは、呼び出される時と場合が異なるだけにすぎません。getChildren メソッドを実装するには、AdapterFactoryContentProvider に対し、指定した親の URI の子を返すよう要求するだけです。リスト 2 に、getChildren メソッドを示します。getElements メソッドの場合は、単にこの getChildren メソッドの呼び出しを返します。

リスト 2. getChildren の実装
...
public Object[] getChildren(Object parentElement) 
{
    if (parentElement instanceof IFile)
    {
        String path = ((IFile)parentElement).getFullPath().toString();
        URI uri = URI.createPlatformResourceURI(path, true);
        parentElement = resourceSet.getResource(uri, true);
    }
    return super.getChildren(parentElement);
}
...

ツリー・ビューにコンテンツを表示するために重要となる次の 2 つのステップは、ツリー・ビュー内のオブジェクトが表示する必要のある子を持っている場合にそれを判断できるようにすること、そして一連の子を親オブジェクトに関連付けられるようにすることです。こうすることにより、ドメイン・オブジェクトが展開/縮小表示されているかに関わらず、ビューアーによってドメイン・オブジェクトの状態を制御できるようになります。この例の場合、ドメイン・オブジェクトは一連の子パッケージを持つ Ecore モデルということになります。これらの子パッケージに含まれるのは、同じ親パッケージに関連付けられたクラス・オブジェクトです。リスト 3 に実装方法を示します。

リスト 3. getParent の実装
...
public Object getParent(Object element)
{
    if (element instanceof IFile)
        return ((IResource)element).getParent();
    return super.getParent(element);
}

public boolean hasChildren(Object element) 
{
    if (element instanceof IFile)
        return true;
    return super.hasChildren(element);
}
...

ここで、使用されているリソースを解決できないというエラーが表示される場合があります。このエラーを解決するには、Dependencies タブで必要なプラグインに org.eclipse.core.resources を追加します。また、コンテンツ・プロバイダーがビュー内のファイル・リソースの URI を取得できるようにするためには、プラットフォームのリソース・セットを使用する必要もあります。そこで、単一の静的リソース・セットの実装を作成し、MNViewContentProvider クラスに private static ResourceSetImpl resourceSet = new ResourceSetImpl(); を追加して、この実装を使ってこれらのリソースを取得できるようにします。

ラベル・プロバイダーについては、作業を EMF.Edit フレームワークの AdapterFactoryLabelProvider クラスに委任するという点ではコンテンツ・プロバイダーの形式と同じです。コンテンツ・プロバイダーの場合と同じくラベル・プロバイダーもドメイン・オブジェクトをその引数として受け取りますが、ラベル・プロバイダーが返すのは、ビューアー内でこのオブジェクトに関連付けられなければならない Image または String です。ビューアーを多少カスタマイズして、名前とアイコンを取得するタスクを委任する場合とオブジェクトに独自のアイコンを提供する場合とをプログラムによって制御することもできますが、カスタマイズはこの記事の目的ではないのでやめておきます。ここで必要なのは、EMF モデルが提供する画像とテキストだけです。ラベル・プロバイダーを実装するには、コンテンツ・プロバイダー用に取得したアイテム・プロバイダーのリストを指定してスーパークラスを呼び出し、この AdapterFactoryLabelProvider スーパークラスにオブジェクトの画像または説明を要求する呼び出しを委任します。

リスト 4. ラベル・プロバイダー
...
public MNViewLabelProvider() 
{
    super(MNComposedAdapterFactory.getAdapterFactory());
}

public Image getImage(Object element) 
{
    return super.getImage(element);
}

public String getText(Object element) 
{
    return super.getText(element);
}
...

すべてを結び付ける

ここまでのところで、プラグインにコンテンツ拡張を実装するという作業は完了しました。このコンテンツ拡張をビューが利用できるようにするプロセスも、同じく 2 段階で行います。第一段階は、この特定のコンテンツ拡張を使用していることを通知するイベントを定義するという作業です。そして第二段階で、ナビゲーター・コンテンツをビューにバインドします。

クラスに記述されたドメイン・モデルを表示可能なことを CNF に通知するには、<possibleChildren /> および <triggerPoints /> 要素をナビゲーター・コンテンツ拡張に追加します。この 2 つの要素は、Eclipse の中核となる式として簡単にプラグインの xml ファイルに定義することができます。この例の場合、ナビゲーター・コンテンツが呼び出されるのはビューアーに EMF モデルのインスタンスであるオブジェクトが含まれる場合です。現在目的としているのは Ecore モデルをナビゲートすることだけなので、この式はリソースの拡張が Ecore であるかどうかをチェックする単純なものにします。possibleChildren要素は、拡張がビューアー内のオブジェクトに親を提供できる場合を指定します。この例でこれらのオブジェクトに該当するのは EMF モデルのオブジェクトまたはリソースのインスタンスです。

リスト 5. triggerPoints 要素と possibleChildren要素
<triggerPoints> 
    <or>
        <and> 
            <instanceof value="org.eclipse.core.resources.IResource"/> 
            <test 
                forcePluginActivation="true" 
                property="org.eclipse.core.resources.extension" 
                value="ecore"/> 
        </and> 
    </or>
</triggerPoints> 
<possibleChildren> 
    <or> 
        <instanceof value="org.eclipse.emf.ecore.resource.Resource"/>
        <instanceof value="org.eclipse.emf.ecore.EObject"/>  
    </or> 
</possibleChildren>

最後に、IResource モデルのコンテンツ拡張をビューアー・コンテンツのバインディングに組み込んだときと同じ手法で、作成したナビゲーター・コンテンツをビューにバインドします。

ナビゲーター・コンテンツ com.ibm.navigator.example.ModelNavigator.emfModelContent を実際のビューにバインドするには、Extensions タブから以下の作業を行います。

  • org.eclipse.ui.navigator.viewer 要素を展開します。
  • viewerContentBinding 要素を展開します。
  • 以下の手順で新しいコンテンツ拡張を作成します。
    • includes 要素を右クリックします。
    • New > contentExtension を選択します。
    • パターンを前に定義した navigatorContent (com.ibm.navigator.example.ModelNavigator.emfModelContent) に設定します。
  • Save をクリックします。

EMF モデルのコンテンツを共通ナビゲーターに追加するのに必要なステップはこれでほとんど完了しました。後は、ModelNavigator プラグインが IResource モデルと EMF Ecore モデルのオブジェクトを表示できるようにするだけです。


ナビゲーターのテスト

EMF プロジェクトでのテスト

このテストを実行するには、お使いのランタイム Eclipse ワークスペースに既存の EMF プロジェクトをインポートする方法、または新しいプロジェクトを作成する方法のいずれかを選択することができます。EMF Developers Guide のチュートリアルには UML モデルをインポートする新規プロジェクトの作成方法が記載されているので、このプロジェクトをダウンロードしてセットアップすることもできます。

ModelNavigator プラグインが機能していることを確認するため、新規のビューで ModelNavigator プロジェクトを実行して Ecore モデルを表示します。このテストを行うには、モデル・ナビゲーター (Model Navigator View) に表示される EMF プロジェクトを作成する必要があります。ここに記載する例では、SchoolLibrary の UML ファイル (「参考文献」を参照) を使用します。

ModelNavigator プロジェクトを Eclipse アプリケーションとして実行した後、以下の手順に従います。

  • Window > Show View > Other をクリックして新規ワークベンチに ModelNavigator ビューを表示します。
  • Model Navigator カテゴリーを探し、Model Navigator View をクリックします。
  • OK をクリックします。

ランタイム・ワークスペースで EMF プロジェクトを作成する手順は以下のとおりです。

  • File > New > Project をクリックして新規プロジェクトを作成します。
  • Eclipse Modeling Framework 要素を展開します。
  • EMF プロジェクトをクリックし、Next を選択します。
  • プロジェクトに SchoolLibrary という名前を付けて、Next を選択します。
  • モデルのインポーターとして Rose class model を選択し、Next を選択します。
  • モデルの URI として、schoollibrary.mdl ファイルをダウンロードした場所までブラウズします。
  • Next をクリックします。
  • 図 5 に示すように library パッケージと SchoolLibrary パッケージを選択します。
  • Finish をクリックします。
図 5. EMF テスト・プロジェクトの作成
EMF テスト・プロジェクトの作成

すべてが計画通りに行けば、モデル・ナビゲーター (Model Navigator View) に新規 SchoolLibrary プロジェクトが表示されるはずです。さらに重要な点は、ナビゲーター・コンテンツが正常に機能している場合、プロジェクトの model ディレクトリーを展開してナビゲーター・ビュー内で library および SchoolLibrary Ecore ファイルのコンテンツをナビゲートできるということです。ナビゲーターは図 6 のように表示されます。

図 6. ModelNavigator プラグインのテスト
ModelNavigator プラグインのテスト

更新の同期

EMF モデルのコンテンツをビューアーに表示するという目標は達成しました。しかし実際に Ecore ファイルをエディターで開いて変更を開始したとしても、現状のままでは何も起こりません。ModelNavigator プラグインが表示中のモデルに加えられた変更を認識しないため、モデルを更新しないからです。モデルが更新されるようにするには、ビューアーがエディター内での変更を認識してビューアー自体を更新するようにしなければなりません。このような同期を行わせる 1 つの手段は、リソース変更リスナーをコンテンツ・プロバイダーに追加することです。以降のセクションでは、そのために必要なステップを概説します。

リソース変更リスナー

コンテンツ・プロバイダーをモデルの変更に反応させるには、コンテンツ・プロバイダーがモデルのファイル・リソース (この例では Ecore ファイル) での変更をリッスンできるようにします。そのために必要なのは IResourceChangeListener および IResourceDeltaVisitor インターフェースを MNViewContentProvider クラスに実装することです。

リソースが変更され、リソースの変更を表す一連のイベントが渡されると resourceChanged(IResourceChangeEvent event) メソッドが呼び出されます。この変更一式が提供するのは、時間の差分としてのリソース・ツリーの変化です。これを利用すれば、リソース・ツリー内での変更をウォークスルーし、変更されたリソースのタイプに応じて必要なアクションを判断することができます。resourceChanged(IResourceChangeEvent event) メソッドの実装方法はリスト 6 のとおりです。このメソッドを実装することによって、変更されたリソース・セットを取得し、リソースの差分にアクセスできるようになります。

リスト 6. resourceChanged メソッド
...
try 
{
    IResourceDelta delta = event.getDelta();
    delta.accept(this);
} 
catch (CoreException e) 
{
...

リソースの差分にアクセスされると、visit(IResourceDelta delta) メソッドが呼び出されます。このプラグインでは、変更されたリソースが Ecore の拡張子を持つ IResource ファイルである場合のみを対象としています。変更されたリソースが Ecore モデルであれば、変更が反映されるように ModelNavigator プラグインを更新しなければなりません。そのためには変更済みファイルからリソースを取得し、そのリソースが表すモデルをリロードします。

リスト 7. visit メソッド
...
IResource changedResource = delta.getResource();
if (changedResource.getType() == IResource.FILE 
   & changedResource.getFileExtension().equals("ecore"))
{
    try
    {
        String path = ((IFile)changedResource).getFullPath().toString();
        URI uri = URI.createPlatformResourceURI(path, true);
        Resource res = resourceSet.getResource(uri, true);
        res.unload();
        res.load(resourceSet.getLoadOptions());
    }
    catch(IOException ie)
    {
        System.out.println("Error reloading resource - " + ie.toString());
    }	
    return false;
}
return true;
...

リソース変更リスナーを利用するには、このリスナーをコンテンツ・プロバイダーに追加し、適当なときに削除する必要があります。そこで、MNContentProvider クラスでコンストラクターに ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); を挿入してリスナーを追加します。また、このクラスの dispose メソッドには ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); を挿入します。これでリソースの変更に応じた更新が行われるようになるはずですが、これは変更を処理する方法としては極めて単純なものであることを忘れないでください。この方法ではコンテンツ・プロバイダーで影響のあったモデルを更新するだけで、モデルの依存関係を調べて該当する要素を更新することまではしていません。これは、ナビゲーターを常に更新された状態にするための基本的方法です。

モデル更新のテスト

Ecore モデルでの変更がビューアーに反映されていることをテストするため、サンプル・アプリケーションを再び実行して変更を加えてみます。

ModelNavigator プロジェクトを Eclipse アプリケーションとして実行した後、以下の手順に従います。

  • model ディレクトリーの下にある schoollibrary.ecore までナビゲートします。
  • schoollibrary.ecore ファイルを該当するエディターで開きます。
  • schoollibrary.ecore ノードを展開します。
  • SchoolLibrary パッケージのノードを右クリックします。
  • New Child > EClass を選択します。
  • Properties タブで、この新しいクラスに名前 (Test) を設定します。
  • ファイルを保存します。

上記の操作によってリソース変更リスナー・コードがトリガーされて ModelNavigator プロジェクトを実行し、モデルが更新されるはずです。すると ModelNavigator ビューが自動的にリロードされます。ツリーを展開して schoollibrary.ecore ファイルまでナビゲートし、上記で行った変更が表示されていることを確認してください (図 7 を参照)。

図 7. リソースが更新された ModelNavigator プラグイン
リソースが更新された ModelNavigator プラグイン

さらに高度なステップ

最後にもう 1 つ、むしろ簡単に行える ModelNavigator プラグインの拡張としては、複合アダプター・ファクトリーを利用するための拡張があります。私たちが作成したナビゲーターは Ecore モデルを表示しますが、これ以外の EMF モデルを表示させるにはどうしたらいいでしょう。これは、EMF.Edit フレームワークが実装されているおかげで意外と簡単に行えます。つまり、対象とするドメイン・モデルのアダプター・ファクトリーを ComposedAdapterFactory に追加し、これらの新しいモデルに対応したトリガーを設定するという最小限の変更で、他のドメイン・モデルのサポートを追加することができます。

複合アダプター・ファクトリーを拡張する

例えば、genmodel オブジェクトをナビゲーターに追加するには、ただ単にコンテンツ・プロバイダーとラベル・プロバイダーが使用するアダプター・ファクトリーのリストにジェネレーター・モデルのアイテム・プロバイダーを追加するだけです。MNComposedAdapterFactory クラスでは、factories.add(new GenModelItemProviderAdapterFactory());createFactoryList() メソッドに追加します。その他にモデル・ナビゲーターで必要となる変更は唯一、このコンテンツ・プロバイダーをトリガーするタイミングをナビゲーターのコンテンツに指示することだけです。以下のように、トリガー・ポイント (triggerPoints) と同じ方法を使ってリソースの拡張子をチェックします。

リスト 8. genmodel のトリガー・ポイント
<and> 
    <instanceof value="org.eclipse.core.resources.IResource"/> 
    <test 
        forcePluginActivation="true" 
        property="org.eclipse.core.resources.extension" 
        value="genmodel"/> 
</and>

現行のトリガーの直下に上記の新しいトリガー式を追加することで、オブジェクトが IResource のインスタンスであり、Ecore の拡張子を持っている場合、あるいは IResource のインスタンスであり、genmodel の拡張子を持っている場合にナビゲーターのコンテンツを関与させています。このプロジェクトを上記で概説したように再度実行すると、Ecore モデルと Generator EMF モデルをナビゲートできるようになっていることがわかるはずです (図 8 を参照)。ただし、これは私たちの極めて単純なリソース更新の枠組みでは計画外のことなので、ModelNavigator ビューのそれぞれのモデル同士で更新が同期されることはありません。

図 8. 複数のモデルを使用した ModelNavigator プラグイン
複数のモデルを使用した ModelNavigator プラグイン

まとめ

ビューアーに EMF モデルを表示するプラグインを作成するタスクは、これで完了です。Eclipse が提供するフレームワークを使用してこのプロセスを簡単に行えるようにする方法を実演し、さらにプラグインに単純な機能を追加する方法についても触れました。この記事で取り上げたのは、これらのフレームワークが提供する機能のほんの一部です。読者のみなさんには是非、Eclipse コンポーネントをさらに掘り下げて調べてみることをお勧めします。


ダウンロード

内容ファイル名サイズ
Codeos-eclipse-emf.ModelNavigator.zip9.5KB

参考文献

学ぶために

製品や技術を入手するために

議論するために

  • Eclipse に関する質問を議論するための最初の場所として、Eclipse Platform newsgroups があります (このリンクをクリックすると、デフォルト Usenet ニュース・リーダー・アプリケーションが起動され、eclipse.platform が開きます)。
  • Eclipse newsgroups には Eclipse を利用し、拡張することに関心を持つ人達のために、さまざまなリソースが用意されています。
  • developerWorks blogs から developerWorks コミュニティーに加わってください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=260610
ArticleTitle=EMF モデルのコンテンツをナビゲートするための Eclipse プラグインを作成する
publish-date=09042007