2009年9月11日星期五

Smooks tools – 通过EMF模型生成其属性GUI

概述

Smooks configuration editor 在早期的版本是一个具有2个页面的多页编辑器。除了其中一个文本页,另外一个页面是一个基于Master-Details的编辑器,称为Design Page。

Smooks configuration editor 是利用EMF,通过Smooks所定义的配置文件的XSD创建对应的EMF模型,所有的EMF模型相关代码都放在了org.jboss.tools.smooks.core 插件项目中。

在上面所提到的设计页面如下图所示:

smookstools_message_filter_page

该页面左侧是一个TreeViewer,右侧则在选中左侧Viewer中的item后显示其属性编辑界面。

而左侧TreeViewer的输入模型正是通过EMF Resource读取Smooks配置文件后得到的EMF模型,其中所有的节点都是EMF模型(或者被emf.edit包装过的模型),右侧的Details界面显示了被选中EMF模型的属性。

熟悉Eclipse开发的读者应该知道,在创建Master-Details类型的FormPage时,我们需要把模型类型以及它所对应的GUI注册一遍,这样一来,当Master的Selection改变的时候,它才能够利用新的模型类型取出对应的GUI,然后重新创建Details的GUI。

Smooks configuration editor 虽然也是使用Master-Details,但它并没有为每一个模型注册对应的GUI页面,而是用这些模型的共同的父类:EObject作为模型类型进行注册,这意味着,每当左侧TreeViewer发生了SelectionChange后,Details页面每次都只能从注册的页面中得到同一个GUI对象,这个GUI对象就是:org.jboss.tools.smooks.configuration.editors.SmooksStuffPropertyDetailPage

但这并不意味着左侧的Details页面不会根据不同模型而改变。Details页面有一个selectionChange的方法,是当模型选择发生改变后触发的。SmooksStuffPropertyDetailPage实现了这个方法:

public void selectionChanged(IFormPart part, ISelection selection) {
    Object oldModel = getModel();
    setOldModel(oldModel);
    …………
    this.itemPropertySource = (IItemPropertySource) editingDomain.getAdapterFactory().adapt(getModel(),
            IItemPropertySource.class);
      ………
      ………
    if (getOldModel() != getModel()) {
        if (propertyComposite != null) {
            propertyComposite.dispose(); // remove the old properties’ GUI
            propertyComposite = new Composite(propertyMainComposite, SWT.NONE);
        }
        createStuffDetailsComposite(propertyComposite); // recreate the GUI for new model
    }
}

从代码中可以看出,每当模型改变的时候,如果是一个新的模型,而不是当前模型,那显示模型属性的Composite会被dispose掉,然后重新创建一个新的Composite对象,再根据新模型创建新的模型属性GUI。

如何根据EMF模型创建属性GUI.

我们都知道,emf.edit会为每一个EMF模型生成一个XXXXXXXItemProvider,然后生成一个XXXXItemProviderFactory的类,来维护为这些ItemProvider。

这些ItemProvider具有很多功能,它实现了很多常见的为GUI显示准备的接口,比如:IItemLabelProvider, ITreeItemContentProvider , IItemPropertySource。其中IItemPropertySource正是获取模型属性的接口。

Smooks configuration editor在初始化的时候,创建了AdapterFactoryEditingDomain,并把这些ProviderFactory注册到这个EditingDomain中,这样一来,就可以通过EditingDomain获得对应的XXXItemProvider。

SmooksStuffPropertyDetailPage正是通过ItemPropertySource获得每一个属性的Descriptor,然后根据这些Descriptor来创建出对应的GUI。

创建Property的GUI分成两种形式:

1. 创建默认的Property GUI。这种方法会为每一个对应的属性模型创建出默认的GUI。

2. 使用注册好的PropertyUICreator创建。通过PropertyUICreator来创建出属性的GUI,如果Creator所创建出的GUI为NULL,SmooksStuffPropertyDetailPage会通过第一种方法创建出GUI来。

PropertyUICreator是一个接口:org.jboss.tools.smooks.configuration.editors.IPropertyUICreator,只要实现了这个接口,并把模型类型和该接口的实力注册到单态类PropertyUICreatorManager中,SmooksStuffPropertyDetailPage就能根据模型类型获得IPropertyUICreator的实例。

现在回到代码中,在SmooksStuffPropertyDetailPage中有一个createStuffDetailsComposite的方法:

protected void createStuffDetailsComposite(Composite detailsComposite) {
    try {
        .......

        // Get the PropertyUICreator instance
        IPropertyUICreator creator = PropertyUICreatorManager.getInstance().getPropertyUICreator(getModel());
        // Get the Property Descriptor from IItemPropertySource instance.
        List<IItemPropertyDescriptor> propertyDes = itemPropertySource.getPropertyDescriptors(getModel());
        if (creator != null) {
            // Create the Property Extention-GUI via PropertyUICreator
        }

        for (int i = 0; i < propertyDes.size(); i++) {
            IItemPropertyDescriptor pd = propertyDes.get(i);
            EAttribute attribute = (EAttribute) pd.getFeature(getModel());
            if (attribute.isRequired()) {
                // Create the required attribute GUI first
                AttributeFieldEditPart editPart = createAttributeUI(detailsComposite, pd, creator);
                ........
            }
        }
        for (int i = 0; i < propertyDes.size(); i++) {
            IItemPropertyDescriptor pd = propertyDes.get(i);
            EAttribute attribute = (EAttribute) pd.getFeature(getModel());
            if (!attribute.isRequired()) {
                // Create the un-required attribute GUI
                AttributeFieldEditPart editPart = createAttributeUI(detailsComposite, pd, creator);
                .......
            }
        }
        if (creator != null) {
            // Create attribute GUI via PropertyUICreator
        }
        ........
    } catch (Exception e) {
        // ignore
    }
}

 

如果从代码中不容易看出整个创建流程,下图能帮助你去理解:

myImage

 

根据PropertyDescriptor创建GUI

在SmooksStuffPropertyDetailPage中有一个方法,是负责根据IPropertyDescriptor创建GUI的。

protected AttributeFieldEditPart createAttributeUI(Composite detailsComposite,
        IItemPropertyDescriptor propertyDescriptor, IPropertyUICreator creator) {
    final IItemPropertyDescriptor itemPropertyDescriptor = propertyDescriptor;
    EAttribute feature = (EAttribute) itemPropertyDescriptor.getFeature(getModel());
    AttributeFieldEditPart editPart = null;
    if (createDefault) {
        EClassifier typeClazz = feature.getEType();
        boolean hasCreated = false;
        if (typeClazz instanceof EEnum) {
            ....
        }
        if (typeClazz.getInstanceClass() == String.class) {
            ......
        }
        ......
    }

    return editPart;
}

不难看出,根据输入Property的Class类型,创建出不同的GUI。而创建这些对应GUI的具体实现,是在 SmooksUIUtils中实现的。

值得一提的是,所创建出来的GUI,都会自动添加监听器,去监听GUI的变化(比如,文本框的变化),然后根据变化将值回写到Property中,我们看SmooksUIUtils中一个代码片段 (from line 945 ) :

     if (itemPropertyDescriptor != null) {
            // add modify listener to the ValueText control
            valueText.addModifyListener(new ModifyListener() {
                public void modifyText(ModifyEvent e) {
                    // Get the value of this property
                    Object editValue = getEditValue(itemPropertyDescriptor, fm);
                    if (editValue != null) {
                        String vt = valueText.getText();
                        if (vt == null || vt.length() == 0) {
                            // if the text is null , un-set the property value.
                            itemPropertyDescriptor.setPropertyValue(fm, null);
                        } else {
                            // set the new value to this property
                            if (!editValue.equals(vt)) {
                                itemPropertyDescriptor.setPropertyValue(fm, vt);
                            }
                        }
                    } else {
                        // set the new value to this property
                        itemPropertyDescriptor.setPropertyValue(fm, valueText.getText());
                    }

                }
            });
        }

ModelPanelCreator

ModelPanelCreator其实是将SmooksStuffPropertyDetailPage中创建属性GUI的代码复制了出来,实现了一个独立的类,这样就可以在其他的非Master-Details的GUI中使用了。

该类主要应用可以参考:

org.jboss.tools.smooks.configuration.editors.NewOrModifySmooksElementDialog

org.jboss.tools.smooks.configuration.editors.SmooksConfigurationOverviewPage

org.jboss.tools.smooks.edimap.editors.EDIMapFormPage

没有评论:

发表评论