以GefTree为例解释一下如何结合使用gef+emf(2)

创建gef框架。
按照基本习惯定义gef各部分包名:



Gef是一个标准的MVC框架,模型层变化,以EditPart为中枢,驱动界面显示层的改变。这就需要建立EditPart与模型层的关联,和EditPart与显示层的关联。
下面介绍EMF模型如何与EditPart建立基本关联。
创建EMFBaseEditPart。
package org.rufus.gef.emf.examples.tree;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.rufus.gef.emf.model.tree.tree.TreePackage;

abstract public class EmfBasePart extends AbstractGraphicalEditPart {

	private org.rufus.gef.emf.examples.tree.EmfBasePart.Adapter adapter;

	@Override
	public void activate() {
		super.activate();
		adapter = new Adapter();
		(((EObject) getModel()).eAdapters()).add(adapter);

	}

	private class Adapter extends AbstractAdapter {
		public void notifyChanged(Notification notification) {
			if (TreePackage.eINSTANCE.getTree_Root().equals(
					notification.getFeature())
					|| TreePackage.eINSTANCE.getTreeContainer_Node().equals(
							notification.getFeature())
							|| TreePackage.eINSTANCE.getDiagram_Trees().equals(
									notification.getFeature())) {
				refreshChildren();
			} else if (TreePackage.eINSTANCE.getTreeNode_Id().equals(
					notification.getFeature())
					|| TreePackage.eINSTANCE.getTreeNode_Name().equals(
							notification.getFeature())) {
				refreshVisuals();
			}
		}
	}

	@Override
	public void deactivate() {
		(((EObject) getModel()).eAdapters()).remove(adapter);
		super.deactivate();
	}
}

这里的关联建立主要在内部类Adapter中实现,为了使代码清晰,我创建了一个超类AbstractAdapter,这个类没有任何作用仅仅是将没必要实现的代码隐藏掉。
在EMF中通知机制是通过Notification发送出来的,Notification对象中记录着相应得改变,每种类型的变化都有特有的Feature来指示。这部分不理解的可以在代码运行起来后多调试几次。
通知对象建立好了,下面就需要把这个通知对象与EditPart建立关联。
方法activate与deactivate。是在EditPart创建和移除时分别执行的两个方法。所以在这里加入对模型的监听,是比较恰当的,在deactivate中除去监听器的做法,可以减少整个应用内存中的对象引用,适当的提高效率。
创建EditPartFactory与PaletteFactory并将两个Factory注册在Editor中。
Editor中注册代码:
public class TreeEditor extends GraphicalEditorWithPalette {
	protected void configureGraphicalViewer() {
		super.configureGraphicalViewer();
		scalableRootEditPart = new ScalableRootEditPart();
		getGraphicalViewer().setRootEditPart(scalableRootEditPart);
		getGraphicalViewer().setEditPartFactory(new TreeEditPartFactory());
……………………
	}
	protected PaletteRoot getPaletteRoot() {
		if (root == null)
			root = TreePaletteFactory.createPalette();
		return root;
	}
}


PaletteFactory中有一个需要注意的地方:我们需要创建自己的CreationFactory,如果大家接触过普通的gef就会发现这个区别。原因是EMF的Model构造与普通javabean情况下的构造稍有不同。有兴趣的可以对比我的CreationFactory与gef.SimpleFactory

以上几个类已经将GEF有EMF的基本关联建立完毕。剩余部分的关联仅在Command中涉及创建,修改,删除具体Model时有所关联。

介绍控制器EditPart的创建:DiagramEditPart,TreeEditPart,TreeContainEditPart,TreeNodeEditPart。一般情况下。模型的数量与part的数量是1对1 的。特殊需求也可以不遵守。每个EditPart都需要继承基类EmfBasePart。
如大家了解的EditPart是负责Figure(表现层)的创建,与具体逻辑操作,EditPolicy的加载。
以DiagramEditPart为例:
package org.rufus.gef.examples.tree.parts;

import java.util.List;

import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.ConstrainedLayoutEditPolicy;
import org.eclipse.gef.editpolicies.NonResizableEditPolicy;
import org.eclipse.gef.requests.CreateRequest;
import org.rufus.gef.emf.examples.tree.EmfBasePart;
import org.rufus.gef.emf.model.tree.tree.Diagram;
import org.rufus.gef.examples.tree.editpolicy.DiagramContainerEditPolicy;
import org.rufus.gef.examples.tree.figures.DiagramLayoutManager;

/**
 * @author shily Created on Feb 16, 2009
 */
public class DiagramPart extends EmfBasePart {
	@Override
	public Command getCommand(Request request) {
		return super.getCommand(request);
	}

	@Override
	public EditPart getTargetEditPart(Request request) {
		return super.getTargetEditPart(request);
	}

	@Override
	protected IFigure createFigure() {
		Figure f = new Figure();
		f.setLayoutManager(new DiagramLayoutManager());
		return f;
	}

	@Override
	protected List getModelChildren() {
		return this.getDiagram().getTrees();
	}

	private Diagram getDiagram() {
		return (Diagram) this.getModel();
	}

	@Override
	public void performRequest(Request req) {	
		super.performRequest(req);
	}

	@Override
	protected void createEditPolicies() {

		installEditPolicy(EditPolicy.LAYOUT_ROLE,
				new ConstrainedLayoutEditPolicy() {

					@Override
					protected Command createChangeConstraintCommand(
							EditPart child, Object constraint) {
						// cant move
						return null;
					}

					@Override
					protected Command getCreateCommand(CreateRequest request) {
						return null;
					}

					protected EditPolicy createChildEditPolicy(EditPart child) {
						// selection able
						return new NonResizableEditPolicy();
					}

					@Override
					protected Object getConstraintFor(Point point) {
						// TODO Auto-generated method stub
						return null;
					}

					@Override
					protected Object getConstraintFor(Rectangle rect) {
						// TODO Auto-generated method stub
						return null;
					}

				});
		// tree create able
		installEditPolicy(EditPolicy.CONTAINER_ROLE,
				new DiagramContainerEditPolicy());
	}

}

但是这里需要讲两个方法getModelChildren(),和policy. createChildEditPolicy()。
方法getModelChildren是在每次刷新子元素时需要访问的方法,我们需要提供相应的子元素集,gef默认提供了一个空list。有些好的设计,会将这个方法设计到BaseEditPart中。如GMF。
方法createChildEditPolicy()是给子Part创建默认Policy。有些人解释是可以做到统一管理,父part就需要管理子part。但是我的理解有些出入。不管怎样,请大家写代码是保持一个统一的风格。不要对代码的维护造成影响。
顺便说一下figure的布局类DiagramLayoutManager,请感兴趣的同学自己阅读吧。这是表现层的一部分。
为了做出tree的效果,我制作了2个figure。TreeNodeContainerFigure和NodeFigure这部分的代码很简单,TreeNodeContainerFigure中提供expand和collapse方法,NodeFigure重写了paintFigure方法。稍稍阅读就可以理解了。为ContainerFigure制作了一个LayoutManager,主要是一些布局算法,有些方法还结合了Editpolicy一起使用。特性的东西不再赘述。

一个包含model,Editpart,figure,editpolicy等基本结构的gef框架便构建好了。

下面说一下Emf的特性,数据的持久化。

在以前的例子中(不是使用emf模型的例子)。我并没有做数据的持久化,也就是说,每一次编辑并没有保存在文件里。使用了emf就可以利用它自身的特性。具体代码见Editor类。
package org.rufus.gef.examples.tree.editors;
/**
 * @author shily Created on Feb 16, 2009
 */
public class TreeEditor extends GraphicalEditorWithPalette {
	。。。。。。。。。。。。。。。。。。。。
	/**
	 * @see org.eclipse.gef.ui.parts.GraphicalEditor#initializeGraphicalViewer()
	 */
	protected void initializeGraphicalViewer() {

		// emf getresource

		FileEditorInput ei = (FileEditorInput) this.getEditorInput();
		ResourceSet resourceSet = new ResourceSetImpl();
		IFile file = (IFile) getEditorInput().getAdapter(IFile.class);
		URI fileURI = URI.createPlatformResourceURI(file.getFullPath()
				.toString(), true);

		EObject object = null;
		try {
			Resource resource = resourceSet.getResource(fileURI, true);
			object = resource.getContents().get(0);
		} catch (Exception e) {
			// resource has nothing
			object = TreeFactory.eINSTANCE.createDiagram();
		}
		// TreeFactory.eINSTANCE.createDiagram()
		getGraphicalViewer().setContents(object);

		getGraphicalViewer().addDropTargetListener(
				new TemplateTransferDropTargetListener(getGraphicalViewer()));
			。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

	}

	

	/**
	 * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor)
	 */
	public void doSave(IProgressMonitor monitor) {

		// emf resource save
		// Save only resources that have actually changed.
		//
		final Map<Object, Object> saveOptions = new HashMap<Object, Object>();
		saveOptions.put(Resource.OPTION_SAVE_ONLY_IF_CHANGED,
				Resource.OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER);

		// Do the work within an operation because this is a long running
		// activity that modifies the workbench.
		//
		WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
			// This is the method that gets invoked when the operation runs.
			//
			@Override
			public void execute(IProgressMonitor monitor) {
				// Save the resources to the file system.
				//

				boolean first = true;
				// Create a resource set
				//
				ResourceSet resourceSet = new ResourceSetImpl();

				// Get the URI of the model file.
				//
				IFile file = (IFile) getEditorInput().getAdapter(IFile.class);
				URI fileURI = URI.createPlatformResourceURI(file.getFullPath()
						.toString(), true);

				// Create a resource for this file.
				//
				Resource resource = resourceSet.createResource(fileURI);

				// Add the initial model object to the contents.
				//
				EObject rootObject = (EObject) getGraphicalViewer()
						.getContents().getModel();
				if (rootObject != null) {
					resource.getContents().add(rootObject);
				}

				// Save the contents of the resource to the file system.
				//				
				try {
					resource.save(saveOptions);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		try {
			// This runs the options, and shows progress.
			//
			new ProgressMonitorDialog(getSite().getShell()).run(true, false,
					operation);
			// fire saved
			this.getCommandStack().markSaveLocation();
			commandStackChanged(null);
		} catch (Exception exception) {
			// Something went wrong that shouldn't.
			//		
		}

	}	
}


注:我的实现并没有考虑多文件,以解异常等等复杂情况。是简单的例子。如果考虑复杂情况,代码可能会更复杂,可供参考的是GMF的默认实现,这里只是展现一下emf的强大功能。

你可能感兴趣的:(eclipse,mvc,算法,框架,UI)