精彩的人生

好好工作,好好生活

BlogJava 首页 新随笔 联系 聚合 管理
  147 Posts :: 0 Stories :: 250 Comments :: 0 Trackbacks
本文是原创作品,如有转载,请注明出处!

 

GEF编辑器的人,不知道有没有发现这样一个问题:每当作一个新的编辑器的时候,有很多代码都和以前的类似。

我发现了这个问题,很多CommandPolicy,包括EditPart都很类似,所以我经常采用Copy&Paste然后修改的方法来加快开发速度。

Eclipse采用的是插件扩展机制,做一次扩展就可以向Eclipse贡献一个新的功能。同理,GEF编辑器中,画板里的可编辑模型,是否也能这样添加呢?

让我们沿着这个思路走下去。

 

定义模型

初步的构想,仍然是继续前两篇文章的足迹,更改原来的例子。

 

模型的定义:
emfgef3_1.JPG

 

最开始做这个例子的时候,我将NodeAbstract设置为了true,因为我希望它就是一个抽象类,我将写RectangleNodeEllipseNode继承Node,他们才会出现在编辑器里。但是后来发现那样做的话,文件确实可以编辑,但是文件保存之后,再次用EMF序列化为对象的时候会出错。

什么?不信?那你自己试试。

这里,我给Node加了一个属性instance,它表示这个对象的实现类。它将给我们代来一些麻烦。

 

创建模型工程

 

与前面的文章一样,利用这个ecore文件创建一个EMF项目,并生成模型代码。Ok,就把代码放在这里,不再改动它了。这个工程的名称是nodenew

 

创建编辑器工程

 

如果说前面的工程仅创建了“抽象模型”,那么本工程就仅创建了基于前面模型的一个编辑器框架

创建一个名为nodenew.gef的插件项目,其他的全部按默认设置。首先,这个插件依赖于nodenew。将nodetest中的commandeditpartconnectionhelppropertiesuicopy过来,将Connection.javaModelManager.javacopy过来,修改代码,尽可能的减少那些红叉。

创建一个扩展点,我们将利用它来初始化编辑器画板。

emfgef3_2.JPG


 

这个扩展点的用意很明确,就是贡献模型并设置模型与EditPart的对应关系。

 

创建一个接口类,所有使用这个扩展点的EditPart需要实现这个接口:

MyEditPart.java

public interface MyEditPart extends NodeEditPart{
    
public void setConnection(Object model);
    
public void removeConnection(Object model);
}

 

创建一个类,从扩展点中找到模型与EditPart的对应关系:

 

ModelToEditPartMap.java

public class ModelToEditPartMap {
    
    
private static Hashtable map = getTable();
    
    
public static Hashtable getTable(){
        
if(map==null){  
            map
=new Hashtable();
            IExtensionRegistry registry 
= Platform.getExtensionRegistry();
            IExtensionPoint point 
= registry.getExtensionPoint(MyConstants.FACTORY_EXTIONPOINT_ID);
            
if (null == point){
                System.err.println(
"No extension point called "+MyConstants.FACTORY_EXTIONPOINT_ID+" is found!");
                
return null;
            }

            IExtension[] extensions 
= point.getExtensions();
            
for (int i = 0; i < extensions.length; i++){
                IConfigurationElement[] elements 
= extensions[i].getConfigurationElements();
                
for (int j = 0; j < elements.length; j++){
                    
if (elements[j].getAttribute(MyConstants.ATTR_TARGETEDITORID).equals(MyConstants.EDITOR_ID)){
                        String eleType 
= elements[j].getName();
                        
if (eleType.equals(MyConstants.ELEMENT_NODEPART)){
                            
try{
                                map.put(elements[j].getAttribute(MyConstants.ATTR_MODELCLASS), elements[j]);
                                System.out.println(
"add a node " + elements[j].getAttribute(MyConstants.ATTR_NAME) + "to map");
                            }

                            
catch(Exception e){
                                e.printStackTrace();
                            }

                        }

                    }

                }

            }

        }

        
return map;
    }


}

 

创建一个类,从扩展点中得到编辑器画板里的元素。

 

FactoryExtension.java

public class FactoryExtension {    
    
private static ArrayList factory = getFactory();
    
    
public static ArrayList getFactory() {
        
if(factory==null){  
            factory
=new ArrayList();
            IExtensionRegistry registry 
= Platform.getExtensionRegistry();
            IExtensionPoint point 
= registry.getExtensionPoint(MyConstants.FACTORY_EXTIONPOINT_ID);
            
if (null == point){
                System.err.println(
"No extension point called "+MyConstants.FACTORY_EXTIONPOINT_ID+" is found!");
                
return null;
            }

            IExtension[] extensions 
= point.getExtensions();
            
for (int i = 0; i < extensions.length; i++){
                IConfigurationElement[] elements 
= extensions[i].getConfigurationElements();
                String pluginId 
= extensions[i].getNamespace();
                Plugin a 
= null;
                
try {
                    a 
= extensions[i].getDeclaringPluginDescriptor().getPlugin();
                }
 catch (InvalidRegistryObjectException e1) {
                    
// TODO Auto-generated catch block
                    e1.printStackTrace();
                }
 catch (CoreException e1) {
                    
// TODO Auto-generated catch block
                    e1.printStackTrace();
                }

                a.getBundle();
                
                
                
                
for (int j = 0; j < elements.length; j++){
                    
if (elements[j].getAttribute(MyConstants.ATTR_TARGETEDITORID).equals(MyConstants.EDITOR_ID)){
                        String eleType 
= elements[j].getName();
                        
if (eleType.equals(MyConstants.ELEMENT_NODEPART)){
                            
if(!Boolean.parseBoolean(elements[j].getAttribute(MyConstants.ATTR_VISIBLE))){
                                
continue;
                            }

                            
try{
                                ImageDescriptor descriptor 
= null;
                                
try{
                                    
if(elements[j].getAttribute(MyConstants.ATTR_ICON)!=null)
                                        descriptor 
= AbstractUIPlugin.imageDescriptorFromPlugin(pluginId, elements[j].getAttribute(MyConstants.ATTR_ICON));
                                }
 catch(Exception e){
                                    e.printStackTrace();
                                }

                                
if(descriptor == null){
                                    descriptor 
= GefPlugin.getImageDescriptor(MyConstants.IMG_DEFAULT);
                                }

                                
                                ToolEntry tool 
= new CombinedTemplateCreationEntry(
                                        elements[j].getAttribute(MyConstants.ATTR_NAME), 
                                        
"Create a new Node"
                                        elements[j].getAttribute(MyConstants.ATTR_MODELCLASS), 
                                        
new MyCreationFactory(elements[j]), descriptor,descriptor);

                                factory.add(tool);
                                System.out.println(
"add a node " + elements[j].getAttribute(MyConstants.ATTR_NAME));
                            }

                            
catch(Exception e){
                                e.printStackTrace();
                            }

                        }

                    }

                }

            }

        }

        
return factory;
    }

    
    
}

 

上面的类中使用的CreationFactory是自己重写的一个类,它的目的在于,当用户选中画板中的一个对象的时候,发出了一个createrequest,我们创建一个对象,封装在这个request里,当编辑器截获这个request的时候,就直接得到了新创建的这个模型对象。

 

MyCreationFactory.java

public class MyCreationFactory implements CreationFactory{
    
    
private IConfigurationElement element;
    
    
public MyCreationFactory(IConfigurationElement element){
        
this.element = element;
    }

    
    
public Object getNewObject() 
                
        
try {
            Object classname
=null
            
            
try {
                classname 
= WorkbenchPlugin.createExtension(element, MyConstants.ATTR_MODELCLASS);
            }
 catch (CoreException e) {
                
// TODO Auto-generated catch block
                e.printStackTrace();
            }

            
            
if(classname instanceof Class)
                System.out.println(
"class");
            
            
return classname;
//            
        }
 catch (InvalidRegistryObjectException e) {
            
// TODO Auto-generated catch block
            e.printStackTrace();
        }
 
        
return null;
    }

    
public Object getObjectType() {        
        
return element.getAttribute(MyConstants.ATTR_MODELCLASS); 
    }


}

 

在这里碰到了一个关于ClassLoader的问题。假如不使用WorkbenchPlugin.createExtension(element, MyConstants.ATTR_MODELCLASS);

从当前的插件项目中load这个class会出现ClassNotFound异常。目前的这个解决方法不是很好,正在寻找更好的解决方案。

 

接着,修改NodeEditorPaletteFactory,从扩展点中初始化编辑器画板:

private static PaletteContainer createShapesDrawer() {
    PaletteDrawer componentsDrawer 
= new PaletteDrawer("Shapes");
    CombinedTemplateCreationEntry component
=null;
    ArrayList factory 
= FactoryExtension.getFactory();
    
if(factory!=null && factory.size()>1){
        
for(int i=0; i<factory.size(); i++){
            component 
= (CombinedTemplateCreationEntry) factory.get(i);
            componentsDrawer.add(component);
        }

    }

    
return componentsDrawer;
}

 

最后,修改NodesEditPartFactory,有些对象需要从扩展点中得到EditPart对象:

private EditPart getPartForElement(Object modelElement) {
        
if (modelElement instanceof Diagram) {
            
return new DiagramEditPart();
        }

        
if (modelElement instanceof Connection) {
            
return new ConnectionEditPart();
        }

        
        EditPart result 
= null;
        
if(modelElement instanceof Node){
            
            String classname 
= ((Node)modelElement).getInstance();
            IConfigurationElement element 
= (IConfigurationElement)ModelToEditPartMap.getTable().get(classname);
            
try {                
                result 
= (EditPart) WorkbenchPlugin.createExtension(element, MyConstants.ATTR_PARTCLASS);
            }
 catch (CoreException e) {
                
// TODO Auto-generated catch block
                e.printStackTrace();
            }

            
        }

        
        
if(result==null){
        
            
if(ModelToEditPartMap.getTable().containsKey(modelElement.getClass().getName())){
                
try {
                    IConfigurationElement element 
= (IConfigurationElement)ModelToEditPartMap.getTable().get(modelElement.getClass().getName());
                
                    result 
= (EditPart) WorkbenchPlugin.createExtension(element, MyConstants.ATTR_PARTCLASS);

                }
catch (CoreException e) {
                    
// TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }

        }

        
        
if(result!=null && result instanceof MyEditPart){
            ((MyEditPart)result).removeConnection(modelElement);
            ((MyEditPart)result).setConnection(modelElement);            
            
return result;
        }

        
        
throw new RuntimeException(
                
"Can't create part for model element: "
                
+ ((modelElement != null? modelElement.getClass().getName() : "null"));
    }

 

这个时候,编辑器的框架已经搭建好了,运行一下看看吧。
emfgef3_3.JPG

 

可以看到,画板里的Connection是一直都有的,但是却没有Node对象。

 

创建自己的扩展

 

再建一个新的插件项目,名为nodenew.my,它依赖于前面的2个插件。

 

首先创建模型对象:

 

EllipseNode.java

public class EllipseNode extends NodeImpl{
    
public EllipseNode(){
        
super();
        
this.instance = this.getClass().getName();
    }

}


RectangleNode.java

public class RectangleNode extends NodeImpl{
    
public RectangleNode(){
        
super();
        
this.instance = this.getClass().getName();
    }

}

 

接着创建EditPart

 

EllipseNodeEditPart.java

public class EllipseNodeEditPart extends NodesEditPart implements MyEditPart{
    
protected IFigure createFigure() {
        IFigure f 
= new Ellipse();
        
        f.setOpaque(
true); // non-transparent figure
        f.setBackgroundColor(ColorConstants.green);
        
return f;
    }

    
    
public void removeConnection(Object model){
        
if(TargetAddConnectionTable.getInstance().contains((Node) model)){
            List l 
= TargetAddConnectionTable.getInstance().getValue( (Node)model);
            
if(l!=null && l.size()>0){
                
for(int i=0; i<l.size(); i++){
                    Connection c 
= (Connection) l.get(i);
                    
this.getModelSourceConnections().add(c);
                    TargetAddConnectionTable.getInstance().remove(c);
                }

            }

        }

    }
    
    
    
public void setConnection(Object model){
        
if(((Node)model).getNext()!=null && ((Node)model).getNext().size()>0){
            
for(int i=0; i<((Node)model).getNext().size(); i++){
                Connection c 
= new Connection();
                c.setSource((Node) model);
                c.setTarget((Node) ((Node)model).getNext().get(i));
                
this.getModelTargetConnections().add(c);
                TargetAddConnectionTable.getInstance().add(c, (Node) c.getTarget());
            }

        }

    }


}

 

RectangleNodeEditPart.java

public class RectangleNodeEditPart extends NodesEditPart implements MyEditPart{
    
protected IFigure createFigure() {
        IFigure f 
= new RectangleFigure();
        f.setOpaque(
true); // non-transparent figure
        f.setBackgroundColor(ColorConstants.green);
        
return f;
    }

    
    
public void removeConnection(Object model){
        
if(TargetAddConnectionTable.getInstance().contains((Node) model)){
            List l 
= TargetAddConnectionTable.getInstance().getValue( (Node)model);
            
if(l!=null && l.size()>0){
                
for(int i=0; i<l.size(); i++){
                    Connection c 
= (Connection) l.get(i);
                    
this.getModelSourceConnections().add(c);
                    TargetAddConnectionTable.getInstance().remove(c);
                }

            }

        }

    }
    
    
    
    
public void setConnection(Object model){
        
if(((Node)model).getNext()!=null && ((Node)model).getNext().size()>0){
            
for(int i=0; i<((Node)model).getNext().size(); i++){
                Connection c 
= new Connection();
                c.setSource((Node) model);
                c.setTarget((Node) ((Node)model).getNext().get(i));
                
this.getModelTargetConnections().add(c);
                TargetAddConnectionTable.getInstance().add(c, (Node) c.getTarget());
            }

        }

    }


}

 

声明本次扩展:

 

plugin.xml

<extension
         
point="nodenew.gef.editpartfactory">
      
<nodepart
            
modelclass="nodenew.model.EllipseNode"
            name
="nodenew.my.nodepart1"
            partclass
="nodenew.editpart.EllipseNodeEditPart"
            targetEditorId
="nodenew.ui.NodesEditor"
            visible
="true"/>
      
<nodepart
            
modelclass="nodenew.model.RectangleNode"
            name
="nodenew.my.nodepart1"
            partclass
="nodenew.editpart.RectangleNodeEditPart"
            targetEditorId
="nodenew.ui.NodesEditor"
            visible
="true"/>
   
</extension>

 

看看结果吧
emfgef3_4.JPG

 

其他

 

本例仍然利用两个action来打开编辑器。

 

不知道这次的研究成果是不是可以发表成学术论文呢??我觉得挺有创意的,呵呵。

 

源码

 

点击下载

 

运行环境

 

JDK 1.4

Eclipse 3.1

EMF

GEF

posted on 2005-12-15 15:57 hopeshared 阅读(3070) 评论(1)  编辑  收藏 所属分类: EMF&GEF

Feedback

# re: emf&gef之三nodenew 2005-12-15 18:00 marmot
- -!!  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: