2007年10月26日

        如果两个插件出现双向的独立性关联就形成了循环依赖,Dengues利用Eclipse扩展点方式解决这个问题。

        首先来介绍一下原理。Eclipse在启动的时候会将所有的扩展点实现加载到一个注册表里面,这里注册的东西可以是一个类,就像是我们通过扩展点的方式实现一个Viewer一样,我们不仅要写入相应的icon还要写入相关的指定的类。
            

            
        同样道理,如果我们可以定义一个扩展点,在Eclipse启动的时候把实现了这个扩展点的类加载到一个核心插件里。如果别的插件如果要用这个类的话,直接加入对之个核心插件的依赖不就可以了从而回避了对这个插件直接的依赖见下图:

        
        在图1里A插件要引用B插件里的类,同样B插件也有需求要引用A插件里的类,这样就造成了插件的循环依赖。现在利用扩展点,在A,B插件里实现一个定义于Core插件里的扩展点,在Eclispe启动的时候,实现了此扩展点的类将可以被加载到Core插件里。通过Core的桥梁作用A、B插件便可以实现类的相互引用。这里A、B插件只是对Core有一个单向的依赖关系,通过core插件的帮助A与B之间可以相互调,避免了直接的调用所以也就不会形成循环依赖了。

        一、扩展点的定义:

        点击进入org.dengues.core插件的plugin.xml 文件,打开Extension Point标签,点Add加入一个扩展点输入如下信息:
        
        在Definition里加一个叫做Service的element,并加入两条use栏为reuqired的属性如下图所示:

        

        其中serviceClass的Type要选成java,并且在Implements里定义一个接口,这个接口规定了这个扩展点在实现的时候指定的类。这里为IDenguesService。其实接口里什么也没方法也没有定义,只是一个申明而已。
        

        

package org.dengues.core;

public interface IDenguesService {

    String ID 
= "org.dengues.commons.denguesService"//$NON-NLS-1$

    String SERVICE_CLASS 
= "serviceClass"//$NON-NLS-1$

    String SERVICE_ID 
= "serviceID"//$NON-NLS-1$
}


        
        二、实现扩展点。具体怎么用一个扩展点,这里就不用多说了,只重点介绍一下怎么实现IDenguesService类。我们以Dengues里的org.dengues.design.core插件为例。在org.dengues.core里我们我建一个IDenguesService的子接口IDesignerCoreService,并写入我们要向其它plugin公开的方法。
        

public interface IDesignerCoreService extends IDenguesService {

    
public IComponentsFactory getComponentsFactory();

    
public IComponentFilesNaming getComponentFilesNaming();

    
public void initializeTemplates();

    
public ICodeGenerator getCodeGenerator(ICompProcess process);

    
public ICodeGenerator getCodeGenerator();

    
public IJavaETLProcessor getJavaProcessor(ICompProcess process);

    
public IJavaETLProcessor getJavaProcessor();

    
// public Action createStartHsqldbServer(String dbName);

    
public Action createStartHsqldbServer();

    
public boolean checkHsqldbConnection();

    
public void runSqlScript(IFile scriptFile);

    
public DatabaseContainer getHsqlDatabase() throws SQLException;

    
public Connection getCurrentConnection() throws SQLException, CoreException, ClassNotFoundException;
}


         以上这些方法就是org.dengues.designer.core这个插件想要对其它插件公开的方法了,它的实现自然会被写入到这个插件里了。这段代码很多,我们就不列在这里了,如果有兴趣的朋友可以到我们Dengues的google svn去check out代码。写好对IDesignerCoreService的实现以后,我们就可以把它加入到事先我们定义好的扩展点里了,见下图:
        
        

        图中的DesignerCoreService就是IDesingerCoreService的实现。好了,当Eclipse启动的时候它就会把这个类加载到注册表里了,我们可以从这个注册里取到这个类了。那以后如果我们想从这个plugin里向外公开一些方法的话,就可以通过向IDesignerCoreService写入相应的方法,在DesignerCoreService里写入相应的实现就可以了。

        三、从注册表里取出扩展点的类。

        在org.dengues.core里我们写了一个GlobalServiceFactory里面提供了相应的代码:

            

static {
        IExtensionRegistry registry 
= Platform.getExtensionRegistry();
        configurationElements 
= registry.getConfigurationElementsFor(IDenguesService.ID); //$NON-NLS-1$
    }


    
/**
     * Comment method "getService".Gets the specific IService.
     * 
     * 
@param klass the Service type you want to get
     * 
@return IService IService
     
*/

    
public IDenguesService getService(Class klass) {
        IDenguesService service 
= services.get(klass);
        
if (service == null{
            service 
= findService(klass);
            
if (service == null{
                
throw new RuntimeException("GlobalServiceRegister.ServiceNotRegistered" + klass.getName()); //$NON-NLS-1$
            }

            services.put(klass, service);
        }

        
return service;
    }


        如果我们要取刚才定义好的那个IDesignerCoreService的话,我们可以按如下方式取到:

    public IDesignerCoreService getDesignerCoreService() {
        IDenguesService service 
= GlobalServiceFactory.getDefault().getService(IDesignerCoreService.class);
        
return (IDesignerCoreService) service;
    }

        
        其实段代码是写在org.dengues.core插件里的CorePlugin里的,也就是说在任何一个插件里只要加入了对org.dengues.core的依赖都可以通过CorePlguin.getDefault().getDesignerCoreService()来得到IDesignerCoreService的实例了。
 
           
            
        
        

posted @ 2007-11-20 13:19 小张飞刀(Dengues Studio) 阅读(1632) | 评论 (2)编辑 收藏
 
      


       Eclipse里有一项功能就是通过update site直接将插件从网上下载到自己的IDE里,使用起来方便省时,平时只是这样用别人的插件;那如果自己开发了一个插件,想让别人通过这种方式下载并使用可以吗?答案当然是肯定的。

       首先要创建一个feature工程,步骤如下:
       1)在New Project Wizard选择Feature Project,点一下步。
       2)写入feature工程的名字比如:org.dengues.feature
       3)在第二页里保持ID为org.dengues.feature.将名字改为Dengues Feature.
       4)选中要关联的插件,在这里我选择了所有的dengues项目插件,如下:

      
        点击finish这样就创建好了一个feature插件了。这里简单的介绍一下feature有什么用,feature可以把其它的一个或者多个插件组合到一起,以便于用户对插件的加载,管理,命名就像是对一个单元进行操作一样。当然也包括可以方便用于发布到网上。

       做好feature插件以后,就是要往里面写入相应的信息喽,里面包括版权,可访问网站什么的东东,填好以后就可以进入下一步创建一个update site project了。

       创建的步骤很简单就不再详细说明了,工程名就叫org.dengues.update吧。

       创建完成之后,在site map里加入一个category,并将我们刚才做好的feature加到这个category里面。就成了下面的样子:


       选中刚加入的feature选build,完成之后这个update site project的结构就变成了这样。

      

       把这个工程下的所有文件直接拷到要发布的网页服务器上就要以了,这样你就以通过Eclipse访问这个网站来更新你的插件了不信你试,简单吧!
      




posted @ 2007-10-30 17:28 小张飞刀(Dengues Studio) 阅读(1636) | 评论 (5)编辑 收藏
 
        最近做了一个任务,要求把一个grahpical editor里的palette里的内容重新刷新一下,要求是在不关闭editor里前提之下。

       一开始还在怀疑这个能否实现不,不过后来看了看代码,发现这是完全可行的,且看我细细道来:

        先看GraphicalEditorWithFlyoutPalette里的splitter这个成员,它把整个editor分成了两个部分一个就是大的用于GEF画图的那部份;另外一部分很明显就是palette啦!说这么多,看看它的createControl方法就全明白啦:
public void createPartControl(Composite parent) {
    splitter 
= new FlyoutPaletteComposite(parent, SWT.NONE, getSite().getPage(),
            getPaletteViewerProvider(), getPalettePreferences());
    
super.createPartControl(splitter);
    splitter.setGraphicalControl(getGraphicalControl());
    
if (page != null{
        splitter.setExternalViewer(page.getPaletteViewer());
        page 
= null;
    }

}

        其中的setExternalviewer就是放的palette的viewer,说到viewer我的第一个联想就是SWT里的viewer其实不是这样的,这里的viewer其实与一个基于GEF的Graphcial Viewer;也就是说,我们在一个graphical editor里看到的palette是通过drawer2D画上去了,和我们平时GEF里的图形没什么两样。
/**
 * Returns the PaletteRoot for the palette viewer.
 * 
@return the palette root
 
*/

protected abstract PaletteRoot getPaletteRoot();
        再看这个getPaletteRoot方法它为palette viewer提供一个root,那这个root到底是什么呢?我们再继续往下看。沿着palette root的继承树往上找,最后发现了这个:

        一看palette entry的文档就明白了,其实它就是 palette的模型。
/**
 * Root class (statically) for the palette model.
 * 
 * 
@author Pratik Shah
 
*/

public class PaletteEntry {
        当然如果它是GEF的模型,那么必然他就会有listeners一查代码,果真是这样的。
/**
 * A listener can only be added once.  Adding it more than once will do nothing.
 * 
@param listener the PropertyChangeListener that is to be notified of changes
 * 
@see java.beans.PropertyChangeSupport#addPropertyChangeListener(
 *                                                         java.beans.PropertyChangeListener)
 
*/

public void addPropertyChangeListener(PropertyChangeListener listener) {
    listeners.removePropertyChangeListener(listener);
    listeners.addPropertyChangeListener(listener);
}
        那么这个add listener方法被谁用呢?想都不用想了,肯定是被它的edit part 喽,MVC嘛~~~不信看PaletteEditPart.java的activate方法:

/**
 * 
@see org.eclipse.gef.editparts.AbstractGraphicalEditPart#activate()
 
*/

public void activate() {
    
super.activate();
    PaletteEntry model 
= (PaletteEntry)getModel();
    model.addPropertyChangeListener(
this);
    traverseChildren(model, 
true);
}
        
        
        模型的修改必定会被通知到 edit part 里,它再根据具体的情况对viewer进行更新,见下:
/**
 * 
@see java.beans.PropertyChangeListener#propertyChange(PropertyChangeEvent)
 
*/

public void propertyChange(PropertyChangeEvent evt) {
    String property 
= evt.getPropertyName();
    
if (property.equals(PaletteContainer.PROPERTY_CHILDREN)) {
        traverseChildren((List)evt.getOldValue(), 
false);
        refreshChildren();
        traverseChildren((List)evt.getNewValue(), 
true);
    }
 else if (property.equals(PaletteEntry.PROPERTY_LABEL)
            
|| property.equals(PaletteEntry.PROPERTY_SMALL_ICON)
            
|| property.equals(PaletteEntry.PROPERTY_LARGE_ICON)
            
|| property.equals(PaletteEntry.PROPERTY_DESCRIPTION))
        refreshVisuals();
}

        明白了!?说了那么多其实只要一名句话啦:修改一下palette root里palette entry的内容GEF 就会自动的将palette里的表现更新了。在Dengues的项目里,我在GEFComponentEditor.java里加入以下方法,便可以了:
    /**
     * Reset the content of the palette root will cause palette viewer be refreshed.
     * 
     * yzhang Comment method "refreshPalette".
     
*/
    
public void refreshPalette() {

        List
<PaletteContainer> containers = new ArrayList<PaletteContainer>(root.getChildren());

        
for (PaletteContainer element : containers) {
            
if (element instanceof PaletteGroup) {
                
continue;
            }
            root.remove(element);
        }

        CompEditorPaletteFactory.create(factory, root);

    }

       关于这个方法是如何调用的,这就涉及到另外一个话题了,见《如何解决插件之间循环依赖的问题》。
   
       K字好累。Han hanhan .....

posted @ 2007-10-26 13:32 小张飞刀(Dengues Studio) 阅读(1325) | 评论 (0)编辑 收藏