最終更新日: 2004 年 6 月 1 日
この資料は、基本的な EMF (Eclipse モデリング・フレームワーク) の概念を理解している ことを前提とします。EMF に関して詳しくは、 『Eclipse モデリング・フレームワーク (EMF) の概説』を参照してください。
EMF 生成プログラムを使用してコードを作成した EMF ベースのモデルがあり、 モデルおよびコードにユーザー・インターフェースを追加しようとしている場合に、 EMF.Edit フレームワークを使用すると、作業を簡略化することができます。
EMF.Edit は、EMF モデルのエディターをビルドするための、再使用可能な汎用クラスを含む Eclipse フレームワークです。これは、以下のものを提供します。
この資料では、EMF.Edit フレームワークおよび生成ツールの基本概念の概要を説明します。 詳しい情報については、各フレームワーク・クラスの資料に、動作および機能について詳細に説明されていますのでご参照ください。
Eclipse ユーザー・インターフェース・フレームワーク (JFace) には、作成されたモデルを表示するための再使用可能なビューアー・クラス (例えば、TreeViewer、TableViewer) のセットが含まれています。 JFace ビューアーは、モデル・オブジェクトを表示するのに特定のプロトコルに固執する (つまり、特定のインターフェースを実装する) 必要がないので、あらゆる種類のオブジェクト (つまり、任意の java.lang.Object サブクラス) で動作します。 これが可能なのは、ビューアーがモデル・オブジェクトを直接ナビゲートするのではなく、 コンテンツ・プロバイダー と呼ばれるアダプター・オブジェクトを介してモデル・オブジェクト にアクセスするためです。
各ビューアー・クラスは、特定のプロバイダー・インターフェースを実装するコンテンツ・プロバイダーを一つ使用します。例えば、TreeViewer は、以下のインターフェースを実装するコンテンツ・プロバイダーを使用します。
public interface ITreeContentProvider ... { public Object[] getChildren(Object object); public Object getParent(Object object); ... }
基本構造を以下の図に示します。
TreeViewer の仕事は、画面上にオブジェクト (項目と呼びます) のツリーを表示することです。 入力 (ルート) オブジェクトを除くすべてのオブジェクトは、コンテンツ・プロバイダーの getChildren() を呼び出すことによって提供されます。
その他の種類のビューアーも同様に処理されますが、各ビューアーごとに個別のインターフェースを 実装するコンテンツ・プロバイダーを必要とします。 ビューアー・インターフェースはそれぞれ異なりますが、 コンテンツ・プロバイダーは、しばしば複数のインターフェースを同時に実装する場合があり、 同じコンテンツ・プロバイダー・クラスを多種のビューアーに対して使用することができます。
EMF.Edit フレームワークは、 EMF モデルのコンテンツを提供するのに使用できる汎用のコンテンツ・プロバイダー実装クラスを提供しています。クラス AdapterFactoryContentProvider は、コンテンツ・プロバイダー・インターフェースを実装し、ビューアーのモデル・オブジェクト (項目) をナビゲートする方法を知っている EMF アダプターに委譲します。例えば、 ツリー・ビューアーをサポートするのに使用する EMF アダプター・クラスの場合は、以下のような EMF.Edit インターフェースを実装します。
public interface ITreeItemProvider { public Collection getChildren(Object object); public Object getParent(Object object); ... }
このインターフェースと、上記で説明したコンテンツ・プロバイダー・インターフェース ITreeContentProvider とが似ていることに注意してください。AdapterFactoryContentProvider は、コンテンツ・プロバイダー・インターフェースを実装し、(ItemProvider インターフェースを実装する) アダプターを探して、それに必要な項目の要求を委譲します。「オブジェクト」の用語が「項目」に変更されたのは意図的なものです。ビューアーの観点からは、それらは「オブジェクト」というよりも「項目」であるといえます。
EMF の図は以下のようになります。
注: EMF.Edit フレームワークとともに提供される生成プログラムを使用して、指定した EMF モデルの ItemProviderAdapterFactory および ItemProvider クラスを自動的に生成できます。 これに関しては、後で詳しく説明します。
AdapterFactoryContentProvider は、アダプター・ファクトリーとともに構成されています。 このアダプター・ファクトリーは、他の EMF アダプター・ファクトリーと同様に、 特定の型 (この場合は ItemProviders) のアダプターを作成し、位置を指定します。コンテンツ・プロバイダーは、 ItemProviderAdapterFactory 上で adapt(item) を呼び出すことによって、getChildren() などの要求を保守します。 これによって、指定された項目の ItemProvider (adapter) が作成される、または戻されます。それは、必要なインターフェース (この場合は、ITreeItemProvider) の getChildren() メソッドに 単純に委任されます。
AdapterFactoryContentProvider の getChildren() メソッドは以下のようになります。
public Object[] getChildren(Object object) { ITreeItemContentProvider adapter = (ITreeItemContentProvider) adapterFactory.adapt(object, ITreeItemContentProvider.class); return adapter.getChildren(object).toArray(); }
すべてのコンテンツ・プロバイダーのメソッドで、これと同じようなパターンが使用されています。 前述のとおり、 AdapterFactoryContentProvider は、単に、要求を実行する方法が分かっている特定の ItemProvider (アダプター) に、コンテンツ・プロバイダーのメソッドを委譲するのみです。
上記の getChildren() メソッドでは、 adapterFactory.adapt() に渡されるオブジェクトは単純な java.lang.Object です (org.eclipse.emf.ecore.EObject ではありません)。これは、EMF.Edit フレームワークの重要なフィーチャーです。 このフレームワークは、そのモデル自体とは異なる可能性のある EMF モデルのビューを提供するように 慎重に設計されています (つまり、オブジェクトを抑制したり、または余分なファントム・オブジェクトなどが含まれるビュー)。 EMF と非 EMF オブジェクトの混在を可能にするために、アダプター・ファクトリー用のフレームワークのベース・クラスは、以下のような adapt() の実装を提供します。
public Object adapt(Object object, Object type) { if (object instanceof Notifier) return this.adapt((Notifier)object, type); else return object; }
EMF Notifier [1] 以外のオブジェクトが指定された場合は、そのオブジェクト自身が戻されます。この設計により、非 EMF 項目をビューに追加するような ItemProvider は、任意の非 EMF オブジェクトを単純にリターン (例えば、getChildren() メソッドから) することができます。戻されたオブジェクトは、ビューアーが必要な ItemProvider インターフェース (例えば、ITreeItemProvider) を実装していれば、他の EMF 項目とすべて同様に処理できます。
この設計上の特徴は、なぜアダプターではなく、プロバイダー/アダプター・クラスの ItemProvider を呼び出すのが良いのかという理由を明らかにしています。まともなアプリケーションの場合、 ビュー・モデル (つまり、ビューアーのコンテンツ・プロバイダーによって提供されるモデル) は、 「実」(EMF) モデル・オブジェクト (その ItemProvider も EMF アダプター) および、 「ファントム」オブジェクト (ItemProvider はそのオブジェクト自身) が混じったものであることが多くなります。 そのため、すべてのアダプターは ItemProvider も兼ねていますが、 その逆は必ずしも真であるとは限りません。
前のセクションでは、JFace ビューアーがコンテンツ・プロバイダーを使用して、 コンテンツ項目を取得する方法について説明しました。ビューアーで表示する項目のラベル・イメージおよびテキストを取得する場合にも、同様の方法が使用されます。項目そのものにそのラベルを要求する代わりに、ビューアーは、ラベル・プロバイダー (コンテンツ・プロバイダーに類似するもの) と呼ばれる別のオブジェクトを使用します。 例えば、TreeViewer は、 ILabelProvider インターフェースを実装するオブジェクトに委譲して、そのツリーの項目のラベルを取得します。
EMF.Edit フレームワークは、コンテンツを提供するのと同じメカニズムを、EMF モデルのラベル・プロバイダーを実装するのに使用します。 汎用のラベル・プロバイダー実装クラス AdapterFactoryLabelProvider (AdapterFactoryContentProvider とちょうど同様に機能します) は、ILabelProvider インターフェースを、 モデルの ItemProvider (コンテンツの ItemProvider と同様) に委譲します。 拡張された図を以下に示します。
コンテンツおよびラベル・プロバイダーは、同一のアダプター・ファクトリー、 そして結果的に同一の ItemProvider に委譲することができます (通常そのようにします)。 コンテンツ・プロバイダーの場合と同様に、ItemProvider が、処理が実際に行われる場所になります。
EMF.Edit プロバイダー・クラスを使用して、ユーザーは以下のように EMF モデルの TreeViewer を作成します。
myAdapterFactory = ... treeViewer = new TreeViewer(); treeViewer.setContentProvider(new AdapterFactoryContentProvider(myAdapterFactory)); treeViewer.setLabelProvider(new AdapterFactoryLabelProvider(myAdapterFactory));
この TreeViewer は表示することができ、例えば、通常の方法 (JFace 規定) ではエディター・ウィンドウ内に表示できます。
これらはいずれもさほど重要なことではないように思われるかもしれませんが、それは、 ここで示されたことが、すべて他者 (つまり、アダプター・ファクトリー) に委譲しているからです。 ここでは、メソッドを委譲しただけであり、まだ実装していません。 しかし、メソッドの実装はコード生成プログラムを備えた EMF.Edit によってサポートされており、 ほとんどの ItemProvider およびファクトリー・コードは自動的に生成されます。 その前に、まず、ItemProvider がどのように処理を実行するかを見てみましょう。
前のセクションで見たように、実際に EMF モデルのコンテンツを提供する作業は、 そのモデルに接続する ItemProvider アダプターによって行われます。前の図中の ItemProvider アダプターの数および型は、意図的にあいまいに表現されていました。 これは、EMF.Edit フレームワークが、ItemProvider アダプターについて、以下の 2 つの異なるパターンをサポートするためです。
指定されたモデルの ITemProvider は、これらのパターンのいずれか、または両方のパターンの組み合わせで実装できます。
最初のパターンを使用すると、モデル内のすべてのオブジェクトは、それと 1 対 1 に対応するアダプターを持ちます。 各アダプターには、それが適用される唯一のオブジェクトへのポインター (ターゲットと呼ばれる) があります。
図に示すと以下のようになります。
図が示すように、このパターンではアプリケーション内のオブジェクト数が 2 倍になるため、 状態を処理するためにインスタンスを必要とするアプリケーションでのみ意味を持ちます。 そのため、これはステートフル・パターンと呼ばれます。
よりよいアプローチは、余分なオブジェクトをほとんどを必要としない、シングルトン・パターンです。 このパターンは、同じ型のすべての項目に対して唯一の ItemProvider アダプターを使用します。 以下のようになります。
この図では、通常どおり、オブジェクトにはアダプターへのポインターがありますが、 ItemProvider (共用される) には、オブジェクトへの戻りのポインターがありません。前にコンテンツ・プロバイダーのセクションで見た、ツリー ItemProvider インターフェースについて記憶しておられれば、 すべてのメソッドに余分な引き数 (オブジェクト) があったことに気付いておられるかもしれません。
public interface ITreeItemProvider { public Collection getChildren(Object object); public Object getParent(Object object); ... }
オブジェクト引き数は、特にこのパターンをサポートするために、各 ItemProvider インターフェースに追加されました。 ステートフルの場合は、このオブジェクト引き数は、常にアダプターのターゲットと同一になります。
また、シングルトン・アダプター・パターンを「真に」サポート、つまり、すべてのオブジェクトに対する唯一のアダプターを一つだけ用意すればよいのにと疑問に思うかもしれません。 この方法は推奨されません。その理由は、確かに (EMF.Edit フレームワークと互換性がある) 可能なパターンではありますが [2] 、 単純であっても、完全に動的な実装となるためカスタマイズが難しい (多くの煩雑な instanceof()検査を行う必要がある)、と考えられるためです。 その代替手段として、ItemProvider クラスのタイプを決めることにより、その継承階層がモデルの階層を鏡映することになるため、一つのモデルに対してきれいなオブジェクト指向のビュー・コードを実装できる、都合のよいディスパッチ・ポイントが提供されます。
ここまでは、コンテンツ・プロバイダーおよびラベル・プロバイダーを使用して EMF モデルを表示する方法についてのみ見てきました。 EMF.Edit フレームワークには、このほかにも、コマンド・ベースでモデルの編集をサポートする機能があります。 「編集 (edit)」という用語を使用するのは、モデルの単純な「書き込み (write)」ではなく、取り消しが可能な変更という意味です。
EMF.Edit インターフェースである EditingDomain は、EMF モデルの編集のためのアクセスを提供するために使用されます。 もう一つ、EMF.Edit の実装クラスである AdapterFactoryEditingDomain は、コンテンツおよびラベル・プロバイダーのように、ItemProvider に (ItemProviderAdapterFactory を介して) 実装を委譲することで機能します。
図示されているように、コマンド・スタックへのアクセスも提供されます。モデルに対するすべての変更はこのコマンド・スタックを通じて行われます。 Editing Domain は、次の 2 つの基本サービスを提供します。
コンテンツ・プロバイダーおよびラベル・プロバイダーが、表示つまり読み取りプロバイダーであるのに対して、編集ドメイン EditingDomain は、モデルに対する変更つまり書き込みプロバイダーであると 考えるとよいでしょう。 以下に、全体像を示します。
モデルの変更の簡単な例を示します。
クラス Company には、クラス Department に対して、departments という 1 対多の参照があると想定します。 会社のある部門を削除する (例えば、エディターの削除アクションを実装する) 場合は、 以下のコードを書けばよいでしょう。
Department d = ... Company c = ... c.getDepartments().remove(d);
非常に単純ですが、このコードは、まさに変更を行います。
もしも、代わりに EMF.Edit の remove コマンド (org.eclipse.emf.edit.command.RemoveCommand) を使用して部門を削除する場合には、以下のようなコーディングを行います。
Department d = ... Company c = ... EditingDomain ed = ... RemoveCommand cmd = new RemoveCommand(ed, c, CompanyPackage.eINSTANCE.getCompany_Departments(), d); ed.getCommandStack().execute(cmd);
この方法で部門を削除することにより、次のようないくつかの利点があります。
このような方法で、コマンドを幅広く使用すると、 EMF.Edit フレームワークから提供されるあらゆる種類の機能が使用可能になります。
前の例では、簡単に new の呼び出しを使用して RemoveCommand を作成しました。 正しく実行できますが、このコマンドは再使用できません。このコード・フラグメントが 実行するのは、会社からある部門を削除するという、非常に特定された動作です。 もしも、代わりに、例えばあらゆる種類のオブジェクトを削除できる再使用可能な削除アクションを作成するのならば、EditingDomain を使用してこの作業を支援します。
EditingDomain インターフェースには、new の代わりにコマンドを作成するのに 使用できるコマンド・ファクトリー・メソッド createCommand() が含まれています。
public interface EditingDomain { ... Command createCommand(Class commandClass, CommandParameter commandParameter); ... }
このメソッドを使用してコマンドを作成するには、まず CommandParameter オブジェクトを作成してそれにコマンド・パラメーターを設定し、続いて create メソッドを呼び出して、それに希望するコマンド・クラス (例えば、RemoveCommand.class) およびそのパラメーターを渡す必要があります。
そのすべてをクライアントが実行しなくても、すべてのコマンド・クラスには 便利な static の create() メソッドを提供する規則を使用しています。 static create() メソッドを使用すると、次のようにして RemoveCommand を作成および実行することができます。
Department d = ... EditingDomain ed = ... Command cmd = RemoveCommand.create(ed, d); ed.getCommandStack().execute(cmd);
おわかりのように、ここではわずかな構文の変更 (new RemoveCommand から RemoveCommand.create() へ) しか行われていません。 しかし、基本的な相違があります。変更前は、編集ドメインに加えて 3 つの引き数がありましたが、代わりに、 1 つの引き数 (つまり、削除されるオブジェクト) だけを渡しています。 変更後は、このコードの断片はどのような種類のオブジェクトでも削除できるようになっています。 コマンドの作成を編集ドメインに委譲することによって、欠落している引き数はドメインに入力させます。
全体的にどのように機能するか確認するため、RemoveCommand.create() 呼び出しをひととおり実行してみましょう。 前述したように、static な create() メソッドは、ただのユーティリティー・メソッドで、以下のように編集ドメインに委譲を行います。
public static Command create(EditingDomain domain, Object value) { return domain.createCommand( RemoveCommand.class, new CommandParameter(null, null, Collections.singleton(value))); }
次に、AdapterFactoryEditingDomain が要求を受け取って、標準の委譲パターン (前出の AdapterFactoryContentProvider が getChildren() を委譲した時と同様) を使用して、以下のようにして ItemProvider に渡します。
public Command createCommand(Class commandClass, CommandParameter commandParameter) { Object owner = ... // get the owner object for the command IEditingDomainItemProvider adapter = (IEditingDomainItemProvider) adapterFactory.adapt(owner, IEditingDomainItemProvider.class); return adapter.createCommand(owner, this, commandClass, commandParameter); }
注: 実際の createCommand() メソッドを調べてみると、 実物はこれよりかなり複雑であるのに気付くことでしょう。その原因は、実際の createCommand() メソッドでは、 とりわけ、オブジェクトのコレクションを一度に削除するように設計されているからです。 いずれにせよ、概念的にはこれが処理のすべてです。
createCommand() メソッドは委譲を行う ItemProvider にアクセスするのに owner オブジェクトを使用しています (つまり、所有者は adapterFactory.adapt() を呼び出す際に使用されます) 。 この例では、所有者は会社オブジェクト (つまり、削除される部門の親) です。 編集ドメインは、削除されるオブジェクトの ItemProvider の getParent() メソッドを呼び出すことによって、所有者を判別します。
この影響により、削除されるオブジェクトの親の ItemProvider (つまり、元のコード・フラグメントでは会社 c の CompanyItemProvider) のメソッド createCommand() が呼び出されます。 そこで、CompanyItemProvider は、以下のように createCommand() を実装できます。
public class CompanyItemProvider ... { ... public Command createCommand(final Object object, ..., Class commandClass, ...) { if (commandClass == RemoveCommand.class) { return new RemoveCommand(object, CompanyPackage.eINSTANCE.getCompany_Departments(), commandParameter.getCollection()); } ... } }
これで作業は完了ですが、さらに良い方法があります。
すべての ItemProvider クラス (EMF アダプターでもある) は、EMF.Edit の便利な基本クラス ItemProviderAdapter から派生しています。 この基本クラスは、特に createCommand() のデフォルトの実装を提供しています。 これは EMF.Edit フレームワークが提供するすべての標準コマンドの createCommand() を実装するもので、ItemProvider サブクラスに実装されるいくつかの簡単なメソッド (この目的以外でも使用される) を呼び出しています。 これは、デザイン・パターンのテンプレート・メソッドの一つの例です。
私たちの RemoveCommand の例を機能させるのに必要なのは、後は CompanyItemProvider に以下のメソッドを実装することです。
public Collection getChildrenFeatures(Object object) { return Collections.singleton(CompanyPackage.eINSTANCE.getCompany_Departments()); }
上に示すように、このメソッドは、オブジェクトの子の参照に使用される 1 つ以上のフィーチャー (この場合は、departments 参照のみ) を戻します。 このメソッド呼び出しの後、createCommand() のデフォルトの実装は、 使用すべきフィーチャーを推定し(複数の戻りがある場合)、その適切な機フィーチャーを使用して RemoveCommand を作成します。
編集ドメインを介してコマンドを作成するその他の利点としては、 標準コマンドの別のサブクラスや完全に異なる実装をプラグインできること、 および標準のエディターからこれらを選択できることがあります。 例えば、会社から部門を削除した時には、追加のクリーンアップ作業を行わなければならないとします。 これを実現する最も簡単な方法は、以下のように、 RemoveDepartmentCommand という RemoveCommand のサブクラスを作成することです。
public class RemoveDepartmentCommand extends RemoveCommand { public void execute() { super.execute(); // do extra stuff ... } }
このパーツはとても簡単です。
ここで、エディターが (editingDomain.createCommand() を呼び出す) static な RemoveCommand.create() メソッドを new RemoveCommand() の代わりに使用するならば、 以下のようにして、ItemProvider で createCommand() をオーバーライドすることにより、標準の RemoveCommand を RemoveDepartmentCommand に簡単に置換できます。
public class CompanyItemProvider ... { ... public Command createCommand(final Object object, ...) { if (commandClass == RemoveCommand.class) { return new RemoveDepartmentCommand(...); } return super.createCommand(...); } }
実際に、特殊化したいコマンドがあらかじめ定義済みのもの (RemoveCommand など) の場合、置換はさらに簡単です。これは、createCommand() のデフォルトの実装が 各コマンドの作成を、コマンド固有の便利なメソッドに以下のようにディスパッチするためです。
public Command createCommand(final Object object, ... { ... if (commandClass == RemoveCommand.class) return createRemoveCommand(...); else if (commandClass == AddCommand.class) return createAddCommand(...); else ... }
このため、以下のように、createCommand() メソッド自体ではなく createRemoveCommand() をオーバーライドするだけで、 RemoveDepartmentCommand をより簡単に作成することができます。
protected Command createRemoveCommand(...) { return new RemoveDepartmentCommand(...); }
要約すると、重要なポイントは、 編集ドメインとは、コマンド・クラス自身を含むコマンド・パラメーターの調整点であり、 それによってモデル上のあらゆる編集コマンドの振る舞いを簡単に制御できるということです。
ここまでに触れていない内容の 1 つに、変更の通知があります。モデル内でコマンドで何かを変更した後で、ビューアーはどのように更新されるでしょう?ビューアーは、 標準の EMF アダプター通知と、EMF.Edit から提供されるビューアー更新メカニズムの 組み合わせを使用して機能する、というのがその答えです。
AdapterFactoryContentProvider は、作成される際に、自分自身をアダプター・ファクトリーのリスナー (つまり、org.eclipse.emf.edit.provider.INotifyChangedListener) として登録します。アダプター・ファクトリーは org.eclipse.emf.edit.provider.IChangeNotifier インターフェースを実装しています。アダプター・ファクトリーは、次に自分の作成したすべての ItemProvider に 自分自身を渡し、自分がモデルの変更通知の中心となることができます。 AdapterFactoryContentProvider は、(inputChanged() メソッドで) 自分がコンテンツを提供したビューアーを記録することもでき、変更通知を受け取った時にビューアーを更新できます。
以下のダイアグラムは、EMF モデル・オブジェクトにおける変更 (例えば、 会社名の変更) が、アダプター・ファクトリーを介してモデルのビューアーへ戻される過程を示します。
EMF オブジェクトの状態が変更されるごとに、ItemProvider (この場合は CompanyItemProvider) を含むすべてのオブジェクト・アダプターでメソッド notifyChanged() が呼び出されます。 ItemProvider の notifyChanged() メソッドには、 各イベント通知をビューアーに渡すかどうか、渡す場合には結果的にどの更新のタイプになるかを 判別する責任があります。
そのためには、インターフェース IViewerNotification の簡単な実装である ViewerNotification で、対象となる可能性のある通知をラップします。このインターフェースは、以下のように、 基本の Notification インターフェースを拡張しています。
public interface IViewerNotification extends Notification { Object getElement(); boolean isContentRefresh(); boolean isLabelUpdate(); }
これらのメソッドは、ビューアー内のどの項目を更新するか、 そのエレメント以下のコンテンツを更新するかどうか、 およびそのエレメントのレベルを更新するかどうかを指定します。 ItemProvider は、オブジェクトの子およびラベルを決定するので、 ビューアーを効果的に更新する方法についても決定する必要があります。
クラス CompanyItemProvider の notifyChanged() メソッドを、以下に示します。
public void notifyChanged(Notification notification) { ... switch (notification.getFeatureID(Company.class)) { case CompanyPackage.COMPANY__NAME: fireNotifyChanged(new ViewerNotification(notification, ..., false, true)); return; case CompanyPackage.COMPANY__DEPARTMENT: fireNotifyChanged(new ViewerNotification(notification, ..., true, false)); return; } super.notifyChanged(notification); }
この実装では、名前属性を変更するとラベルが更新され、 部門参照を変更するとコンテンツがリフレッシュされます。その他の変更通知は、ビューアーに影響を与えません。
fireNotifyChanged() メソッドは、アダプター・ファクトリーに単純に通知を転送する、 クラス ItemProviderAdapter (すべての ItemProvider アダプターの基本クラス) の 便利なメソッドです[3] 。 アダプター・ファクトリー (変更ノーティファイヤー) は、続けて、自身のすべてのリスナー (この例では、ツリー・ビューアーのコンテンツ・プロバイダー) へ通知をディスパッチします。 最後に、コンテンツ・プロバイダーは、通知の指示に従ってビューアーを更新します。
EMF モデルは、しばしばモデル間参照によって結合されています。 複数の EMF モデルに関するオブジェクトの編集または表示を行うアプリケーションを作成する時には常に、2 つ (あるいはそれ以上) のモデルからなるオブジェクトの結合を適用することが可能なアダプター・ファクトリーが必要になります。
多くの場合、個々のモデルのアダプター・ファクトリーがすでに存在していて、 必要なのはそれらを結合することだけです。 そのために、もう 1 つの EMF.Edit の便利なクラス ComposedAdapterFactory を使用することができます。
ComposedAdapterFactory は、他のアダプター・ファクトリーに共通のインターフェースを提供するために使用します。 ComposedAdapterFactory は、これらのアダプター・ファクトリーに対して、単純に実装を委譲します。
複合アダプター・ファクトリーをセットアップするには、以下のようなコーディングを行います。
model1AdapterFactory = ... model2AdapterFactory = ... ComposedAdapterFactory myAdapterFactory = new ComposedAdapterFactory(); myAdapterFactory.addAdapterFactory(model1AdapterFactory); myAdapterFActory.addAdapterFActory(model2AdapterFactory); myContentProvider = new AdapterFactoryContentProvider(myAdapterFactory); ...
注: EMF.Edit エディターおよび、EMF モデルを生成するためのステップバイステップのチュートリアルが、 『チュートリアル: EMF モデルの生成』で参照できます。
EMF モデル定義が与えられると、EMF.Edit コード生成プログラムは、完全な機能が装備されたエディター・ツールを生成できます。このツールを使用すれば、幾つかの共通ビューアーを使用してモデルのインスタンスを表示したり、モデル・オブジェクトの追加、除去、切り取り、コピー、および貼り付け、または 標準プロパティー・シートにおけるオブジェクトの変更を、「元に戻す」/「やり直し」機能が完全にサポートされた環境で行えます。
EMF.Edit 生成プログラムは、以下を含む、完全に機能するプラグインを生成します。
生成されたエディターは、実行できます。ただし、予想どおりに動作する場合と、しない場合があります (つまり、生成プログラムが行う デフォルト選択が、使用するモデルに適していない可能性があるということです)。それでも、 生成されたコードを何箇所か簡単に微調整することができ、基本的な機能するエディターをすばやく稼働させることができます。
次に、生成されるクラスの中から特に興味深いものを詳しく見てみましょう。
生成される ItemProviderAdapterFactory は、EMF モデルの生成時に生成される AdapterFactory クラスの単純なサブクラスです。
注: 生成される EMF アダプター・ファクトリーは、それぞれの型固有の create() メソッドをディスパッチすることにより、アダプターを作成します。従って、サブクラス (ItemProviderAdapterFactory など) で create() メソッドをオーバーライドする必要があります。 EMF アダプター・ファクトリー (例えば、ABCAdapterFactory) は、 別の生成クラス (ABCSwitch) を使用して、ディスパッチを効果的に実装します。
ステートフル・パターンを使用している場合は、以下のように、 アダプター・ファクトリーの create メソッドは単純に新規オブジェクトを戻します。
class ABCItemProviderAdapterFactory extends ABCAdapterFactoryImpl { ... public Adapter createCompanyAdapter() { return new CompanyItemProvider(this); } ... }
単一パターンが代わりに使用されている場合は、アダプター・ファクトリーは、 単一インスタンスの追跡も行い、各呼び出しごとにこれを戻します。
protected DepartmentItemProvider departmentItemProvider; public Adapter createDepartmentAdapter() { if (departmentItemProvider == null) { departmentItemProvider = new DepartmentItemProvider(this); } return departmentItemProvider; }
モデルの各クラスごとに、対応する ItemProvider クラスが生成されます。 この項目プロバイダーは、標準ビューアー、コマンド、およびプロパティー・シートをサポートするために必要なすべてのインターフェースの混合です。
public class DepartmentItemProvider extends ... implements IEditingDomainItemProvider, IStructuredItemContentProvider, ITreeItemContentProvider, IItemLabelProvider, IItemPropertySource { ... }
モデル・クラスがルートである (つまり、明示的な基本クラスがない) 場合、 生成された項目プロバイダーは、EMF.Edit 項目プロバイダーのベース・クラス ItemProviderAdapter から派生します。
public class EmployeeItemProvider extends ItemProviderAdapter ...
代わりに、モデル・クラスがベース・クラスから継承している場合、生成される項目プロバイダーは、 次のようにベースの項目プロバイダーから派生します。
public class EmployeeItemProvider extends PersonItemProvider ...
多重継承クラスの場合、生成された項目プロバイダーは、(単一継承クラスの場合と同じく) 最初の 基本クラスの項目プロバイダーから派生し、それ以外の基本クラスのプロバイダー機能を実装します。
生成された ItemProvider クラスを調べてみると、その機能の多くが、実際に 項目プロバイダーの基本クラスにおいて実装されていることが分かります。 生成された ItemProvider サブクラスの最も重要な機能を 以下に示します。
生成された Editor および ModelWizard を見ると、すべての他の生成物を標準 JFace コンポーネントと統合し、実際に機能するエディターを作成する方法を示しています。
ModelWizard は、モデルの型の新規リソースを作成するのに使用できます。 あるいは、別の方法で作成した既存のリソースがあれば、 それをデスクトップ・ワークスペースにインポートしてからエディターを起動すれば、ModelWizard を一切使用する必要はありません。
[1] ノーティファイヤーは、 EMF におけるオブジェクト用の基本インターフェースで、アダプターを登録し、それらのアダプターに通知を送信することができます。 これは、すべてのモデル・オブジェクトの基本インターフェースである EObject を拡張します。
[2] 実際には、 EMF アダプター・ファクトリーは継承主導型であるため、EObject を頂点として、 モデル内の任意のレベルでベース・アダプターによってサブクラスを処理することを選択可能です。
[3] ビューアーの変更を通知する機能があるアダプター・ファクトリーの他に、ItemProviderAdapter もまた、 やはり ItemProviderAdapter.fireNotifyChanged() で呼び出しできる (直接の) リスナーを持つことができます。