Zestを用いてEMFモデルインスタンスをグラフィカルに表示する

タスク指向UIを提供するMylynプロジェクトに、ZestというVisualization Toolkitが付属しています。そのZestの主旨から、MylynからGEF(Graphical Editing Framework)に所属を変更しようというプロポーザルがEclipseに提出されているところです。

Zestの特長は、JFaceと同様の使い勝手でノードとエッジから成るグラフィカルビューを容易に作成可能な点です。開発者は、ContentProviderとLabelProviderを実装し、Zestが提供するViewerにそれらをセットするだけで、グラフィカルビューを実現可能です。

今回は、簡単なEMFモデルのインスタンスを表示する、以下のようなグラフィカルビューを、Zestを用いて簡単に実装します。

上記ビューは以下のEMFモデルのインスタンスを表示します。

準備

Zestのインストール

まず、Zestをインストールします。ZestはCoreおよびLayoutから構成され、それぞれ以下のサイトからjarファイルをダウンロードできます。
http://www.eclipse.org/mylyn/zest.php
ただし注意したいのは、上記サイトからダウンロード可能なjarファイルはJDK1.6でコンパイルされたものです。したがって、JDK1.5以下を使用している場合(特にMacOSXユーザの場合)には、上記jarファイルは使用できませんので、EclipseCVSリポジトリからソースコードをチェックアウト/エクスポートする必要があります。

The latest development release is also available from our CVS repository.
Create a new CVS location: dev.eclipse.org/cvsroot/tools
Checkout:
mylyn/sandbox/org.eclipse.mylyn.zest.layouts
mylyn/sandbox/org.eclipse.mylyn.zest.core

EMFモデルの作成

次に、上記EMFモデルを用意し、ModelコードおよびEditコードを自動生成しておきます。

ContentProviderの実装

Zestをインストールできたら、Zestが提供するGraphViewにセットするContentProviderを実装します。GraphViewにセットするContentProviderは、以下の3つインタフェースのうちいずれかを実装する必要があります。

  • IGraphContentProvider
  • IGraphEntityContentProvider
  • IGraphEntityRelationshipContentProvider

IGraphContentProviderでは、エッジを要素として捉えてそのエッジの両端点を返すメソッドを実装します。
IGraphEntityContentProviderおよびIGraphEntityRelationshipContentProvierでは、ノードを要素とし、そのノードの接続相手のノードを返すメソッドを実装します。

ここでは、EMFが自動生成するEditコードに含まれるアイテムプロバイダを有効利用するために、IGraphEntityContentProviderを実装するContentProviderを作成します。以下のように、EMFが提供するAdapterFactoryContentProviderを拡張して簡単にContentProviderを作成します。

public class MyAdapterFactoryContentProvider extends
		AdapterFactoryContentProvider implements IGraphEntityContentProvider {

	EObject rootModel;
	
	public MyAdapterFactoryContentProvider(AdapterFactory adapterFactory, EObject rootModel) {
		super(adapterFactory);
		this.rootModel = rootModel;
	}

	public Object[] getConnectedTo(Object entity) {
		return getChildren(entity);
	}

	@Override
	public Object[] getElements(Object object) {
		ArrayList<EObject> result = new ArrayList<EObject>();
		for (Iterator<EObject> i = rootModel.eAllContents(); i.hasNext();) {
			EObject eObject = (EObject) i.next();
			result.add(eObject);
		}
		return result.toArray();
	}

}

上記コードにおいて、getConnectedTo(Object entity)はIGraphEntityContentProviderによって定義されたメソッドで、引数に渡されたオブジェクトとエッジで接続されるオブジェクトを全て返します。ここでは、EMFによって自動生成したItemProviderが提供するgetChildren()をそのまま利用しています。

注意が必要なのはgetElement(Object object)で、JFaceの他のViewerと異なり、表示する全てのオブジェクトを返す必要があります。

ViewPartの実装

あとは、Eclipseビューを作成します。プラグインプロジェクトを新規作成する際に利用可能な、PDEが提供するサンプルビュー等を参考にしてください。以下は、ZestのGraphViewを利用した、ViewPart.createPartControl(Composite parent)の実装例です。さきほど作成したContentProviderをGraphViewにセットしています。なお、LabelProviderについては、EMFが提供するAdapterFactoryLabelProviderをそのまま利用しています。

public void createPartControl(Composite parent) {
	createModel();
	viewer = new GraphViewer(parent, SWT.NONE);
	viewer.setContentProvider(new MyAdapterFactoryContentProvider(adapterFactory, (EObject) getModel()));
	viewer.setLabelProvider(new AdapterFactoryLabelProvider(adapterFactory));
	viewer.setLayoutAlgorithm(
			new TreeLayoutAlgorithm(
					LayoutStyles.NO_LAYOUT_NODE_RESIZING));
	viewer.setInput(getModel());
}

上記コードにおいて、createModel()は、EMFモデルインスタンスをprogramaticallyに作成しています。
viewer.setLayoutAlgorithm()で、グラフィカルビューに表示するノード群の配置アルゴリズムを指定できます。ここではTreeLayoutAlgorithmを指定しています。他にも以下のような配置アルゴリズムをZestは提供しています。

  • GridLayoutAlgorithm
  • HorizontalLayoutAlgorithm
  • HorizontalTreeLayoutAlgorithm
  • SpringLayoutAlgorithm
  • VerticalLayoutAlgorithm

上記の他に、CompositeLayoutAlgorithmというクラスもあったので、上記の組み合わせも可能かもしれませんね。

以上により、EMFモデルインスタンスをグラフィカルに表示するビューを、Zestを用いて非常に簡単に実現できます。

エッジに付加される文字列が気になりますね。。。ラベルプロバイダを工夫する必要がありそうです。