本页主题: emf&gef之三nodenew 打印 | 加为IE收藏 | 收藏主题 | 上一主题 | 下一主题

hopeshared

状态: 离线
级别: CEC总版
精华: 0
发帖: 412
威望: 79 点
财富: 58 元
在线时间:73(小时)
注册时间:2005-10-16
最后登录:2008-08-28

emf&gef之三nodenew

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

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

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

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



一 定义模型

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



模型的定义:





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

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

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



二 创建模型工程



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



三 创建编辑器工程



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

创建一个名为nodenew.gef的插件项目,其他的全部按默认设置。首先,这个插件依赖于nodenew。将nodetest中的command、editpart、connectionhelp、properties、ui包copy过来,将Connection.java和ModelManager.java也copy过来,修改代码,尽可能的减少那些红叉。

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








这个扩展点的用意很明确,就是贡献模型并设置模型与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.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.getConfigurationElements();
          String pluginId = extensions.getNamespace();
          Plugin a = null;
          try {
            a = extensions.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是自己重写的一个类,它的目的在于,当用户选中画板中的一个对象的时候,发出了一个create的request,我们创建一个对象,封装在这个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"));
  }



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





可以看到,画板里的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>



看看结果吧






五 其他



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



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



六 源码



点击下载



七 运行环境



JDK 1.4

Eclipse 3.1

EMF

GEF


原文地址:http://www.blogjava.net/hopeshared/archive/2005/12/15/24057.html


[p:1]
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Welcome to my BLOG: http://hopeshared.blogjava.net
顶端 时间: 2005年12月15日 16:05 | [楼 主]
usamasara

状态: 离线
级别: CEC高级程序员
精华: 0
发帖: 23
威望: 0 点
财富: 229 元
在线时间:13(小时)
注册时间:2005-12-18
最后登录:2008-09-05

好文阿!!
佩服,真是敢想敢做阿!
顶端 时间: 2006年04月07日 16:20 | 1 楼
bobby

状态: 离线
级别: CEC高级程序员
精华: 0
发帖: 16
威望: 11 点
财富: 218 元
在线时间:9(小时)
注册时间:2006-04-27
最后登录:2006-08-10

正在学习!
先谢了!!
顶端 时间: 2006年05月11日 16:37 | 2 楼
lkitten

状态: 离线
级别: CEC高级程序员
精华: 0
发帖: 33
威望: 11 点
财富: 233 元
在线时间:0(小时)
注册时间:2005-12-25
最后登录:2008-03-07

谢过啦!
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
程序员
顶端 时间: 2007年04月04日 13:26 | 3 楼
homer4503

状态: 离线
级别: CEC程序员
精华: 0
发帖: 30
威望: 1 点
财富: 35 元
在线时间:0(小时)
注册时间:2007-09-18
最后登录:2008-11-16

学习的对象
顶端 时间: 2007年10月26日 20:38 | 4 楼
hotelcn2007

状态: 离线
级别: CEC程序员
精华: 0
发帖: 16
威望: 1 点
财富: 16 元
在线时间:0(小时)
注册时间:2007-11-26
最后登录:2007-11-27

正在学习!
先谢了!!
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
顶端 时间: 2007年11月26日 17:36 | 5 楼
ziyulei

状态: 离线
级别: CEC程序员
精华: 0
发帖: 26
威望: 1 点
财富: 27 元
在线时间:0(小时)
注册时间:2008-01-08
最后登录:2008-04-09

学习中!谢谢
顶端 时间: 2008年03月06日 09:24 | 6 楼
中国Eclipse社区 » 图形与模型


辽ICP备05021625号