`

[zt]使用GEF创建数据库模型编辑器

阅读更多

FROM:http://www.rcp.org.cn/bbs_topic.do?forumID=7&postID=164

原文:http://www.eclipse.org/articles/Article-GEF-editor/gef-schema-editor.html

用过Eclipse相关产品的人经常会看到一些涉及到图形应用的插件,比如Struts、UML、DatabaseDesign相关的插件;或者企业中自己要定制自己的图形化的工作流编辑器,组织结构图等。这些都涉及到图形编辑,以前我们大多用Win32 API或JFC/Java2D技术自己做框架来实现这些功能,复杂且工作量大!
GEF(Graphical Editor Framework)是现在Eclipse领域流行的图形编辑框架,它可以用来给用户提供图形化编辑模型的功能,从而提升用户体验。它同时具有Windows的美观界面和JFC的跨平台能力,并可以与draw2D(Eclispe领域想要取代java2D的图形API)达到良好的结合。
GEF里用到了很多经典的结构模式和设计模式,如MVC,Policy,Command,Observer等,方便的实现Undo/Redo功能等等,通过学习GEF,也可以更好地掌握这些模式.
在本文中,我们将介绍GEF/draw2D,并简介用GEF如何一步步的做一个数据库模型编辑器。


读者朋友们可能已经看到了前面文章所介绍的Eclipse体系结构了,这里还有一张图对GEF与Eclispe平台之间的关系作了描述:SWT和Draw2D的关系就相当于AWT与Java2D的关系,而GEF这个框架是基于ui.views和RCP(见本系列另一篇关于RCP的文章)概念之上的。
 

说到GEF框架,还是要从我们熟悉的MVC模式说起,MVC是一种通用的涉及到UI交互的模式,通常Model代表数据的显示,View则负责在界面上呈现数据,而Controller的职责是处理用户的输入以及根据Model的修改刷新View。一般情况下,一个UI(View)只被用于呈现一个Model,但是在GEF里面任何View可以被用于呈现任意的Model,也就是说在GEF里View-to-Model的映射并不是1-to-1的,在GEF中MVC的定义如下:
 

Model:任何GEF 应用的起点就是Model。它们需要被显示, 编辑和持久化。Model应该对View和Controller的细节一无所知,它仅仅可以把自己的改变负责通知给View和Controller,在GEF中,Model只能被Commands所更新。
View:View包括Draw2D中一种虚拟组件Figure来呈现Model对象, GEF也支持SWT中的TreeItem对象。另外,与View相关的还包括feedback,handle,tooltip等等。所有这些View组件都由Controller来构建和管理它们。
Controller:就是EditPart,EditPart在GEF里面是最关键的组件,负责管理整个View层面的所有组件。后面会有详细 介绍
 

在我们的这个数据库模型编辑器中,我们定义了我们的MVC架构:

我们的Model:
我们的Model分成如下几种:
• Table: 代表一张关系数据库表。 唯一的属性是表名
• Column: 数据库表的列。 我们比较感兴趣的属性是列名和数据类型( 可能是VARCHAR 或整型)。
• Relationship: 表现为两张数据库表之间的主外键关系。
• Schema: 简单地可以代表所有表的分组。
我们的模型很简单, 但至少包括在一个典型的GEF 模型中两种关键的对象关系:
• 在table和schema之间以及table和column之间存在父子关系
• 在不同的节点(Node)之间的连接关系。 在我们的例子中, connection是以主外键       关系的形式

我们的View
在View这一部分的介绍,我们分别介绍Figure和布局管理器这两部分:

Figure
Figure在draw2d 中是一种创建复杂图形的轻量级对象。视图则是由一组反映model的Figures来组建的。 在典型的GEF 应用中, 您通常会创建一组定制的Figure类并实现IFigure 接口。

在我们的应用中有以下的Figure:

EditableLabel: draw2d中Label的子类。 用于为column和table命名。
ColumnsFigure: 所有column的一个容器。
TableFigure: 包含EditableLabel 和ColumnsFigure
SchemaFigure: 在Schema中包含所有TableFigures
另外,我们并未提供任何特别的figure来代表图中的连接线, 我们简单地使用draw2d 的PolylineConnection 类, 来代表一条有0或多个拐点(bend point) 的连接线。

由于表名,列名,列数在一个TableFigure 实例的生命周期内可以增加或减少的, 我们也可以重设我们的ColumnsFigure 和TableFigure的尺寸。这个功能由布局管理器(draw2d 的另一个重要部份)担当。

布局管理器
GEF 提供与Swing和SWT 的LayoutManager不同的一个布局管理框架: 它的职责是处理实现draw2d IFigure的Figure的布局。应用开发者将决定用某个LayoutManager来布局每一个包含子Figure的Figure。

宽泛地讲, 有三类型LayoutManager:
• 结构化布局管理器,比如FlowLayout 和ToolbarLayout, 可以根据子figure的次序来垂直或水平地布局
• 基于约束的布局管理器,譬如XYLayout 和DelegatingLayout 。为每个子figure设置一个Constraint对象。 在XYLayout 情况下, 这个对象是一个Rectangle对象来指定位置和尺寸。
• 使用几何算法的布局管理器, 这种布局为子figure运用一系列的相当复杂算法来计算布局。这种 算法采取一种特殊的数据结构作为譬如Node的安置和path的路由之类几何问题的解决方案。GEF 由DirectedGraphLayout 和CompoundDirectedGraphLayout提供算法。
 

GEF 开发者需要理解特定的LayoutManager被应用于特定的情况。在我们的应用中ColumnsFigure 使用FlowLayout来布局它的Label组件。TableFigure 使用ToolbarLayout 来布局它的子组件(简单地垂直摆放) 。大家可以看一下TableFigure的初始化过程:

java 代码
  1. public TableFigure(EditableLabel name, List colums)   
  2. {   
  3. nameLabel = name;   
  4. ToolbarLayout layout = new ToolbarLayout();   
  5. layout.setVertical(true);   
  6. layout.setStretchMinorAxis(true);   
  7. setLayoutManager(layout);   
  8. setBorder(new LineBorder(ColorConstants.black, 1));   
  9. setBackgroundColor(tableColor);   
  10. setForegroundColor(ColorConstants.black);   
  11. setOpaque(true);   
  12.   
  13. name.setForegroundColor(ColorConstants.black);   
  14. add(name);   
  15. add(columnsFigure);   
  16. }   
  17.   

我们的Controller:
当我们开始谈论控制器才算真正走入GEF领域。 GEF 提供了EditPart来避免Model知道关于View Part(Figure)的细节, 并且反之亦然。

EditParts:

一般情况下,我们需要为每个Model创建一个EditPart实例(通常我们都是通过继承AbstractEditPart来创建我们自己的EditPart),所以我们必须使我们的EditParts的继承结构和Model的继承结构相匹配。每个EditPart (也就是GraphicalEditPart), 会处理一个Model组件, 并且关联一个视图组件。 由于模型和视图完全地被分离, 所有在模型和视图之间的协调必须由EditPart 处理。

在我们的模型编辑器的实现中,有如下的EditPart实现:
• SchemaDiagramPart: 表现为一个Schema实例及其相关的 SchemaFigure
• TablePart: 表现为一个Table并且管理这个Table相关的TableFigure及其子Figure
• ColumnPart: 能够让我们编辑 column label
• RelationshipPart: 表现为一个主外键关系. 一个 RelationshipPart关联两个 TableParts
我们可以用一个EditPartFactory来提供EditPart:
java 代码
  1. public class SchemaEditPartFactory implements EditPartFactory   
  2. {   
  3.     public EditPart createEditPart(EditPart context, Object model)   
  4.     {   
  5.         EditPart part = null;   
  6.         if (model instanceof Schema)   
  7.             part = new SchemaDiagramPart();   
  8.         else if (model instanceof Table)   
  9.             part = new TablePart();   
  10.         else if (model instanceof Relationship)   
  11.             part = new RelationshipPart();   
  12.         else if (model instanceof Column)   
  13.             part = new ColumnPart();   
  14.         part.setModel(model);   
  15.         return part;   
  16.     }   
  17. }   
SchemaDiagramPart, TablePart and ColumnPart都将继承GraphicalEditPart. 另外,TablePart 是一个主外键关系中的一个node, 因此它必须实现NodeEditPart. 最后, RelationshipPart表现为关系中的一个connection part, 所以它必须实现AbstractConnectionEditPart.
在GEF应用中,有以下一些任务EditPart的子类必须实现:
1.提供一个EditPart相关的Figure实例
java 代码
  1. protected IFigure createFigure()   
  2. {   
  3. Table table = getTable();   
  4. EditableLabel label = new EditableLabel(table.getName());   
  5. TableFigure tableFigure = new TableFigure(label);   
  6. return tableFigure;   
  7. }   
2.在继承结构中代表父对象的EditParts必须重写getModelChildren()
java 代码
  1. protected List getModelChildren()   
  2. {   
  3.     return getTable().getColumns();   
  4. }   

3.如果代表父EditPart的Figure并非子EditPart的Figure的直接父类,那么需要实现AbstractGraphicalEditPart.getContentPane(),返回子EditPart的Figure
在我们的例子中可以从一个TableFigure的实例中得到ColumnPart的ColumnFugure
java 代码
  1. public IFigure getContentPane()   
  2. {   
  3.     TableFigure figure = (TableFigure) getFigure();   
  4.     return figure.getColumnsFigure();   
  5. }   

Request:
Request是处理GEF 应用中的一个编辑动作的起点。GEF 能用面向对象的方式处理用户交互和转发这些requests。例如, 当我们从“New Column”这个调色板(palette)按钮拖放一个column到图中的一个表中时,相当于用户与GEF应用的交互,GEF应用在后台将创建request对象。 比如创建column时, GEF 将产生CreateRequest对象。
不同的用户操作将会产生不同的Request类型,一般情况下我们主要有三种类型的request:CreateRequests, GroupRequests and LocationRequests.在GEF API和文档中有详细的描述。 这些request对象将用户操作对模型改变的信息巧妙地封装起来。

EditPolicies and Roles
一个EditPolicy 是EditPart 的扩展, 事实上, 和某些编辑的相对任务是EditPart 传递request给它的委托者(delegate)-EditPolicy ,类似于我们在J2EE开发中经常设计一些特定的RequestProcessor(class或method)来处理前端Servlet传过来的特定的request,。Policy是一种非常好的面向对象设计技巧,可以这么说,EditPolicy承担了绝大部分EditPart的细节工作。另外,每个EditPolicy对应一个role,为了理解EditPolicy,我们看在TablePart中的createEditPolicies()
java 代码
  1. protected void createEditPolicies()   
  2. {   
  3.     installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new TableNodeEditPolicy());   
  4.     installEditPolicy(EditPolicy.LAYOUT_ROLE, new TableLayoutEditPolicy());   
  5.     installEditPolicy(EditPolicy.CONTAINER_ROLE, new TableContainerEditPolicy());   
  6.     installEditPolicy(EditPolicy.COMPONENT_ROLE, new TableEditPolicy());   
  7.     installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new TableDirectEditPolicy());   
  8. }  

这个方法的作用是简单地装饰TablePart的编辑功能。每一次调用installPolicy都是为EditPart注册一个EditPolicy。例如,EditPolicy.CONTAINER_ROLE是与TablePart 相关的,之所以role叫Container是因为我们知道Table包含column, 并且我们的应用程序要创建新的column以及添加这些column到已存在的table。

很多抽象的EditPolicy类提供了一个getCommand(Request request)方法的实现。在ContainerEditPolicy类中我们能够发现如下代码:
java 代码
  1. public Command getCommand(Request request) {   
  2.   if (REQ_CREATE.equals(request.getType()))   
  3. return getCreateCommand((CreateRequest)request);   
  4.   if (REQ_ADD.equals(request.getType()))   
  5. return getAddCommand((GroupRequest)request);   
  6.   if (REQ_CLONE.equals(request.getType()))   
  7. return getCloneCommand((ChangeBoundsRequest)request);   
  8.   if (REQ_ORPHAN_CHILDREN.equals(request.getType()))   
  9. return getOrphanChildrenCommand((GroupRequest)request);   
  10.   return null;   
  11. }   
这里getCommand()使用request类型来决定哪个getXXXCommand() 方法将被调用。 但是在ContainerEditPolicy中, 这些方法是抽象的- 我们必须在具体的EditPolicy类中提供具体的fa方法实现:
java 代码
  1. public class TableContainerEditPolicy extends ContainerEditPolicy   
  2. {   
  3.     protected Command getCreateCommand(CreateRequest request)   
  4.     {   
  5.      //从request得到新对象并确认它是Column的实例   
  6. Object newObject = request.getNewObject();//从request得到新对象并确认它是   
  7.         if (!(newObject instanceof Column))   
  8.         {   
  9.             return null;   
  10.         }   
  11.         Column column = (Column) newObject;   
  12. //得到与TablePart相关的Table对象   
  13.      TablePart tablePart = (TablePart) getHost();   
  14.         Table table = tablePart.getTable();   
  15. //创建相关的Command并设置Table和Column信息   
  16. ColumnCreateCommand command = new ColumnCreateCommand();   
  17.         command.setTable(table);   
  18.         command.setColumn(column);   
  19.         return command;   
  20.     }   
  21. }   
多数情况下,我们的EditPolicy的实现就是简单地使用Request对象生成一个command.
我们为不同的role有不同的EditPolicy实现。例如在我们的应用程序中TableEditPolicy继承ComponentEditPolicy来实现EditPolicy.COMPONENT_ROLE. 并通过实现createDeleteCommand(GroupRequest request)来处理REQ_DELETE.

Commands
相信了解设计模式的人都会对Command这个模式耳熟能详,每一个具体的Command负责对Model的修改,这些Command都继承GEF中的一个抽象基类:org.eclipse.gef.commands.Command,它简单地封装了我们对request的响应,其主要方法有excute(),undo(),redo(),canExcute(),canUndo(),canRedo().
大多数情况下,Command的子类需要实现excute()和undo(),而其他方法是可选的,我们可以看看ColumnCreateCommand的实现:
java 代码
  1. public class ColumnCreateCommand extends Command   
  2. {   
  3.     private Column column;   
  4.     private Table table;   
  5.     public void setColumn(Column column)   
  6.     {   
  7. //为column设置名字和类型   
  8.         this.column = column;   
  9.         this.column.setName("COLUMN " + (table.getColumns().size() + 1));   
  10.         this.column.setType(Column.VARCHAR);   
  11.     }   
  12.   
  13.     public void setTable(Table table)   
  14.     {   
  15.         this.table = table;   
  16.     }   
  17.   
  18.     public void execute()   
  19.     {   
  20. //将新column添加到table   
  21.      table.addColumn(column);   
  22.     }   
  23.   
  24.     public void undo()   
  25.     {   
  26.      table.removeColumn(column);   
  27.     }   
  28. }   

使用Commands比起直接使用EditPolicies来改变model有两个好处:
• Command是很好的OO设计模式 ;
• Command框架支持undo和redo功能;
Command的实现不能够容纳任何对GEF特有的组件如EditParts或EditPolicies的引用。要注意保持Commands和UI逻辑干净的分离

 

传播机制
一旦我们改变model,我们的GEF编辑器需要将这些改变传播到UI。我们需要协调模型,视图和控制器来完成这个工作!
迄今为止,我们讨论了的GEF中Model,View和Controller的功能,但是为了做一个模型编辑器,我们需要让我们的EditPart做更多的事情:
• 需要一个listener来更新model。而model也需要传播EditPart能够接收到的事件响应
• 需要维持与其子EditPart以及Connection之间的关系,并同步其与model的改变。
• 需要更新它管理的Figures以及布局,符合model的改变。
最后要做的事情
最后我们要为这个模型编辑器定义自己的扩展点:
xml 代码
  1. <extension  
  2.          point="org.eclipse.ui.editors">  
  3.       <editor  
  4.             name="%editor.name"  
  5.             icon="icons/editor.gif"  
  6.             extensions="schema"  
  7.             class="com.realpersist.gef.editor.SchemaDiagramEditor"  
  8.             contributorClass="com.realpersist.gef.action.SchemaActionBarContributor"  
  9.             id="Schema Editor">  
  10.       editor>  
  11. extension>  

同样要为它定义一个Wizard来帮助我们创建数据库Schema图
  
xml 代码
  1. <extension  
  2.          point="org.eclipse.ui.newWizards">  
  3.       <category  
  4.             name="GEF (Graphical Editing Framework)"  
  5.             parentCategory="org.eclipse.ui.Examples"  
  6.             id="org.eclipse.gef.examples">  
  7.       category>  
  8.       <wizard  
  9.             availableAsShortcut="true"  
  10.             name="Schema Diagram Editor"  
  11.             icon="icons/editor.gif"  
  12.             category="org.eclipse.ui.Examples/org.eclipse.gef.examples"  
  13.             class="com.realpersist.gef.editor.wizard.SchemaDiagramWizard"  
  14.             id="com.realpersist.gef.editor.wizard.wizard.new.file">  
  15.          <description>  
  16.            Wizard to create an empty or pre-populated schema diagram file   
  17.          description>  
  18.          <selection  
  19.                class="org.eclipse.core.resources.IResource">  
  20.          selection>  
  21.       wizard>  
  22.    extension>  

除了这些内容,我们还有好多东西没能在这里一一呈现,比如Palette, PropertySheet,Outline
 

声明:该文章已发表于《程序员》2006年第五期,如要引用请注明出处。
分享到:
评论

相关推荐

    Eclipse开发使用GEF和EMF(IBM 红皮书)中英文对照版

    3. 图形编辑器的构建:详细步骤指导如何使用GEF创建可交互的图形编辑器,包括图元的创建、布局管理、编辑操作的实现等。 4. EMF模型的生成与使用:讲解如何定义XML Schema,由EMF自动生成Java类,以及如何在代码中...

    EMF_GEF_入门系列

    8. **实例分析**:通过实际项目案例,详细解释如何从零开始构建一个简单的模型编辑器,让学习者能动手实践。 通过这个入门系列的学习,新手开发者将能够熟练运用EMF和GEF构建高效、直观的图形用户界面和模型驱动的...

    GEF 入门版中文教程 源码

    RCP是Eclipse组织提供的一种用于构建桌面应用的框架,而GEF则是用于创建图形化编辑器的工具包。本教程由法语版翻译而来,经过精心整理,以中文形式呈现,确保了国内开发者能够更方便地理解和学习。 GEF的核心概念...

    RCP与GEF课件,介绍RCP和GEF各组成部分和工作原理

    GEF支持创建各种图形用户界面,包括绘图工具、流程图和模型编辑器。 GEF的主要组件包括: 1. **Model**:模型是数据结构,包含了图形元素的信息。它可以是简单的数据结构,也可以是复杂的领域模型。 2. **Edit ...

    eclipse emf&gef

    通过将GEF与EMF结合使用,可以轻松创建高度可定制的图形化编辑器,用于编辑由EMF生成的模型。 **3.3 GEF编辑器开发流程** 1. **定义模型**:首先使用EMF定义模型。 2. **创建视图**:使用GEF创建一个图形化的视图...

    eclipse插件 GEF-SDK-3.4.0.zip

    GEF常被用于创建系统架构图、UML模型、数据库设计工具、电路图编辑器等。它的灵活性和强大功能使其在软件工程、数据可视化和领域特定的图形编辑应用中非常受欢迎。 8. **社区支持与学习资源** Eclipse社区提供了...

    Eclipse-EMF-GEF资料.rar

    通过学习和实践这些资料,开发者可以掌握构建基于 EMF 的模型,使用 GEF 创建图形编辑器,以及如何将 XML Schema 数据与模型集成。这些技术在现代软件工程,尤其是企业级应用和工具开发中,都有着广泛的应用。

    GEF学习资料.zip

    1. **模型(Model)**:在GEF中,模型是数据结构的抽象,它定义了编辑器所操作的对象和它们之间的关系。模型独立于视图和控制器,是应用程序的核心部分。 2. **视图(View)**:视图是模型的可视化表示,由GEF提供...

    eclipse gef ve gmf

    而GMF则是对GEF的增强,专门用于创建复杂的模型驱动的图形编辑器。在Eclipse 3.2这个版本中,这些工具集成了良好的图形建模和可视化编辑能力,对于开发人员来说,它们是构建图形化应用程序和模型的重要工具箱。 ...

    GEF快速入门教程和EMF教程

    ### GEF快速入门教程与EMF教程概览 #### GEF HelloWorld 示例详解 ...此外,还介绍了如何设置项目依赖、创建编辑器以及实现GEF的基本组件。这些步骤不仅适用于本教程的示例,也是构建更复杂图形编辑器的基础。

    GEF入门系列

    通过这个GEF入门系列,你可以逐步掌握如何利用GEF创建强大的图形化编辑器,无论是简单的绘图工具还是复杂的业务流程编辑器,GEF都能提供坚实的技术支持。在实践中不断探索和学习,你将能够充分发挥GEF的潜力,构建出...

    GEF_Tutorial.rar(GEF开发指南)

    **GEF(Graphical Editing Framework)开发指南*...总之,"GEF_Tutorial.rar"是一个宝贵的资源,它将引导你踏入GEF开发的世界,通过实例学习,你将能够熟练地创建出功能丰富的图形编辑器,实现各种复杂的图形用户界面。

    GEF-SDK-3.4.2.zip

    GEF是一个强大的开源库,允许开发者创建富客户端应用,特别是那些需要交互式图形界面的领域,如流程图、网络拓扑图或数据库模型。 这个SDK包含了用于开发图形用户界面的所有必要组件,包括模型-视图-控制器(MVC)...

    GEF资源文件

    GEF是Eclipse平台下开发图形化编辑器的强大工具,它通过提供模型、视图、编辑部件、命令和布局等组件,降低了创建图形界面的复杂度。通过深入理解和熟练应用GEF,开发者可以高效地构建出功能丰富的图形化应用,满足...

    GEF 可用的6个demo

    通过深入研究这些DEMO,开发者可以掌握使用GEF构建图形化编辑器的基本技能,进一步了解如何利用Java和Eclipse平台创建复杂的图形用户界面。每个DEMO都是一个独立的学习单元,通过实践和比较,可以更全面地理解GEF的...

    emf swt runtime gef

    4. 示例代码和项目:可能包括了使用EMF、SWT和GEF创建的示例应用程序或编辑器,帮助开发者理解如何结合使用这些技术。 5. 文档和教程:详细介绍了如何使用这三个框架,包括API参考、用户指南和最佳实践。 通过学习...

    GEF_Demo_Code20170307

    2. **在ViewPart中使用GEF**:通常情况下,GEF主要用于创建图形编辑器(Editor),但本示例特别指出在非Editor的ViewPart中使用GEF。ViewPart是Eclipse RCP(Rich Client Platform)中的一个组件,用于显示各种视图...

    gef小例子(含文档)

    在Eclipse插件开发中,GEF(Generic Editor Framework)是一种用于构建图形化编辑器的框架,它允许开发者创建复杂的图形界面,特别适合构建模型编辑器。EMF(Eclipse Modeling Framework)是Eclipse项目中的一个重要...

    Eclipse插件gef 3.8.0

    - **模型(Model)**:在GEF中,模型是数据结构的核心,它存储了编辑器所表示的信息。模型独立于图形视图,可以采用任何适合的数据结构实现。 - **视图(View)**:视图是模型的可视化表示,它将模型数据转换成用户...

Global site tag (gtag.js) - Google Analytics