Eclipse建模框架(EMF)-教程(官方教程翻译)

Eclipse EMF。本教程介绍了EclipseEMF的使用,它是一个用于建模数据模型并从中创建Java代码的框架。本教程基于Eclipse4.6(EclipseNeon)。

1.模型和Eclipse EMF

1.1.数据模型

数据模型,有时也称为域模型,表示要使用的数据。例如,如果你开发了一个在线航班预订应用程序,你可以用Person、flight、booking等对象来建模你的域模型。EMF工具允许你创建UML图。

一个好的实践是独立于应用程序逻辑或用户界面对应用程序的数据模型进行建模。这种方法导致类几乎没有逻辑和很多属性,例如,Person类可能具有firstName、lastName、Address属性等。

使用EMF,您可以显式地定义域模型。这有助于提供模型的清晰可见性。EMF模型的代码生成器可以在其默认设置中进行调整。在模型发生更改时,它为模型提供了更改通知功能。EMF生成接口和工厂来创建您的对象;因此,它可以帮助您保持应用程序与各个实现类之间的清洁。

另一个优点是,您可以在任何时间点从模型中重新生成Java代码。

1.2.Eclipse建模框架(EMF)

Eclipse建模框架(EMF)是一组Eclipse插件,可用于对数据模型进行建模,并基于此模式生成代码或其他输出。EMF在元模型和实际模型之间有区别。元模型描述了模型的结构。模型就是这个元模型的具体实例。

EMF允许开发人员通过不同的方式创建元模型,例如XMI、Java注释、UML或XML方案。它还允许持久化模型数据;默认实现使用一种称为XML元数据交换的数据格式。

1.3.从EMF模型生成数据

存储在EMF模型中的信息可以用于生成导出的输出。一个典型的用例是使用EMF定义应用程序的域模型,并根据该模型生成相应的Java实现类。EMF框架支持手工安全地扩展生成的代码。

EMF模型(基于模型结构保存真实数据)也可以用于生成不同的输出,例如HTML页面,或者可以在应用程序的运行时对其进行解释。

1.4.元模型-Ecore和Genmodel

EMF元模型由两部分组成;ecore和genmodel描述文件。

ecore文件包含有关已定义类的信息。genmodel文件包含用于代码生成的附加信息,例如路径和文件信息。genmodel文件还包含应该如何生成代码的控制参数。

1.5.Ecore描述文件

ecore文件允许定义以下元素。

EClass:表示一个类,具有零个或多个属性和零个或更多引用。

EAttribute:表示一个具有名称和类型的属性。

EReference:表示两个类之间关联的一端。它有标志来指示它是否表示一个包含和它所指向的引用类。

EDataType:表示属性的类型,例如int、float或java.util.Date

Ecore模型显示了代表整个模型的根对象。该模型具有表示包的子级,其子级表示类,而类的子级表示这些类的属性。

1.6.Ecore描述文件

您可以通过.ecore文件的上下文菜单并选择Initialize ecore Diagram…来创建现有ecore模型的图形表示…​.

image.png

2.安装

通过帮助安装新软件安装EMF…​ Eclipse中的菜单项。选择建模并安装EMF-Eclipse建模框架SDK和Ecore图表编辑器(SDK)。第二个条目允许您创建模型。

image.png

安装完成后重新启动Eclipse IDE。

3.练习:定义一个新的EMF模型并从中创建Java代码

3.1项目和初始模型创建

通过文件新建项目创建一个名为com.vogella.emf.webpage.model的新项目…​ Ecore建模项目。

image.png

输入webpage.ecore作为“域文件名”参数。

image.png

image.png

这将打开一个用于创建EMF模型的可视化编辑器。

image.png

通过菜单打开Window > Show View > Other…​ > Properties。此视图允许您修改模型元素的属性。

单击“类”,然后单击进入编辑器以创建一个MyWebWebpageCategory and Article EClasses。

image.png

使用“属性”节点可以将名为name的属性指定给每个对象。此属性应具有EString类型。

image.pngimage.png

将标题、说明和关键字属性添加到Web和网页模型元素中。

image.png

我们希望在我们的模型中使用数据类型日历。选择数据类型并将其拖动到模型中。将名称Calendar分配给它。使用java.util.Calendar作为类型参数。

image.png

将名为created的新属性添加到Article,并使用新类型Calendar。

选择“参考”并创建一个类似于下图的箭头。请确保将上限设置为*,并标记了Containment属性。

image.pngimage.png

3.2查看Ecore图

关闭关系图并打开网页.ecore文件。结果应该看起来像下面的屏幕截图。

image.png

3.3设置包装

打开网页.genmodel并选择网页节点。将基本包属性设置为com.vogella.emf.webpage.model。

image.png

4.生成域类

4.1.生成Java代码

基于.genmodel文件,您可以生成Java代码。

右键单击.genmodel文件的根节点,然后选择“生成模型代码”。这将在当前项目中创建EMF模型的Java实现。

image.png

4.2.审核生成的代码

生成的代码将包括以下内容:

模型网页 — 接口和创建Java类的工厂

模型.webpage.impl — 模型中定义的接口的具体实现

模型.webpage.util — 适配器工厂

中心工厂具有通过createObjectName()方法创建所有定义对象的方法。

对于每个属性,生成的接口及其实现包含getter和(如果模型定义中允许的话)setter方法。每个setter还生成了一个通知给模型的观察者。这意味着其他对象可以将它们附着到模型上,并对模型中的更改做出反应。

每个生成的接口都扩展了EObject接口。EObject是每个EMF类的基础,是java.lang.Object的EMF等价物。EObject及其相应的实现类EObjectImpl提供了一个轻量级基类,允许生成的接口和类参与EMF通知和持久性框架。

每个生成的方法都用@generated标记。如果您想手动调整该方法,并在下一代运行期间让EMF覆盖该方法,则需要删除此标记。

4.3.更新模型

如果您更改了.ecore模型,那么只需重新加载即可更新.genmodel。

image.png

5.创建EMF编辑器插件

EMF可以生成插件,这些插件提供用于创建新模型实例的向导和允许您输入模型信息的编辑器。

以下假设您已经掌握了开发Eclipse插件的知识。有关Eclipse插件开发的更多信息,请参阅Eclipse插件教程。

5.1.生成编辑/编辑器代码

EclipseEMF允许您为模型创建一个编辑器。打开.genmodel文件,右键单击它并选择Generate Edit Code,然后选择Generate Editor Code。

image.png

已经创建了两个Eclipse插件项目,com.vogella.emf.webpage.model.edit和com.vogela.emf.webpage.model_editor。

5.2.运行插件

选择*.editor项目,然后用鼠标右键单击并选择Run As Eclipse application,使用新插件启动一个新的Eclipse实例。

这应该会启动一个新的Eclipse运行时实例。

5.3.创建您的模型

在新的Eclipse实例中,创建一个称为测试的General类型的新项目和一个名为网站的文件夹。

选择此文件夹,右键单击,选择New > Other…​>Example EMF Model Creation Wizards >Webpage Model.

emfeditor20

将您的模型命名为My.webpage。

image.png

选择作为“模型对象Web”,然后按finish。

image.png

5.4.编辑您的模型

您现在应该看到您的网站.model的编辑器。

image.png

右键单击Web并创建一个新元素。要编辑图元,请使用“属性”视图,该视图位于Window > Show View > Properties.
EMFEditor60

EMFEditor70

保存创建的模型。

6.使用型号代码

6.1概述

生成的模型代码是标准Java代码,并且可以这样使用。下面演示如何基于生成的代码创建对象。

6.2.示例

创建一个名为com.vogella.emf.webpage.usingmodel的新插件项目。将以下依赖项添加到MANIFEST.MF中。

  • org.eclipse.emf.ecore
  • com.vogella.emf.webpage.model

创建以下类。

package com.vogella.emf.webpage.usingmodel;










import com.vogella.emf.webpage.model.webpage.Web;
import com.vogella.emf.webpage.model.webpage.Webpage;
import com.vogella.emf.webpage.model.webpage.WebpageFactory;
import com.vogella.emf.webpage.model.webpage.WebpagePackage;




public class UsingEMFModel {
    public static void main(String[] args) {
        WebpagePackage.eINSTANCE;
        // Retrieve the default factory singleton
        WebpageFactory factory = WebpageFactory.eINSTANCE;
        // create an instance of myWeb
        Web myWeb = factory.createWeb();
        myWeb.setName("Hallo");
        myWeb.setDescription("This is a description");
        // create a page
        Webpage webpage = factory.createWebpage();
        webpage.setTitle("This is a title");
        // add the page to myWeb
        myWeb.getPages().add(webpage);
        // and so on, and so on
        // as you can see the EMF model can be (more or less) used as standard Java
    }

}

  • 在执行其他操作之前,需要调用*PackageImpl.init()方法,因为该方法初始化模型和侦听器。

7.创建JavaDoc

您还可以为您的类和方法生成Javadoc。EMF使用带有特定属性键的注释。添加此项的最简单方法是重新绘制图表。选择一个类并在“文档”部分中维护文档。

javadoc emf10

ecore模型现在如下所示。注释中的键www.eclipse.org/emf/2002/Ge…
javadoc emf20

8.生成方法

默认情况下,EMF为每个类生成getter和setter。您还可以添加操作,例如覆盖方法,例如toString()方法。对于Article,在ArticleImpl中生成了以下toString方法。

    `     * @generated
 */
@Override
public String toString() {
    if (eIsProxy()) return super.toString();
    StringBuffer result = new StringBuffer(super.toString());
    result.append(" (name: ");
    result.append(name);
    result.append(", created: ");
    result.append(created);
    result.append(')');
    return result.toString();
}`

若要覆盖此项,请向Article添加一个名为toString的Operation。在属性中维护EType EString作为返回类型。
method10

添加带有源的注释www.eclipse.org/emf/2002/Ge…

method20

    StringBuffer result = new StringBuffer(super.toString());
    result.append("Article: ");
    result.append(name);
    return result.toString();`

您也可以使用输入参数生成方法,只需将参数及其类型添加到操作中即可。
method30

method40

9.扩展EMF Ecore模型(继承)

9.1概述

EMF允许通过继承来扩展现有模型。下面将定义一个基本模型和基于该基本模型的扩展。例如,这可以用于扩展Eclipse e4应用程序模型。它还将演示如何在不使用ecore工具的情况下直接使用EMFecore模型。

9.2.示例

创建一个新的EMF项目com.vogella.EMF.inheritance新建模项目。在这个项目中,创建一个名为model的文件夹,在这个文件夹中,通过在刚刚创建的项目上选择File new Eclipse Modeling Framework Ecore model来创建一个新模型。将模型命名为base.ecore。选择EPackage作为基础,并维护此包的以下属性。

extend10

右键单击“EPackage base”并选择New Child > EClass.。

维护具有两个EAttribute id和EString类型描述的类MyBaseClass。

extend3

在模型文件夹中创建一个新的Ecore模型extendedmodel.Ecore。将extendedmodel保留为EPackage名称。

extend4

在模型上单击鼠标右键,然后选择“Load resource.”。

extend40

extend50

extend60

创建一个新类 MyExtendedClass.

extend61

现在在Properties视图中设置nameESuperTypes

extend70

添加您的MyBaseClass

extend80

extend90

在MyExtendedClass上维护一个新的EAtribute detailedField

extend100

在extended.ecore.Generated Java代码的基础上创建一个新的genmodel extended.genmodel,您会看到MyExtendedClass已经扩展了MyBaseClass。

10.将空字符串设置为默认值

如何将空字符串设置为EMF字符串属性的默认值并不明显。要将空字符串设置为默认值,请执行以下操作:

  • 选择属性

  • 在属性视图中,单击“Default value Literal”的值字段

  • 不要输入内容

要再次删除此空值,请单击工具栏中的“恢复默认值”。

11.创建EMF发电机模型

如果您的EMF生成模型丢失,您可以创建一个。右键单击.ecore文件,然后选择“New > Other…​ > Eclipse Modeling Framework > EMF Generator model。基于Ecore模型创建webpage.genmodel文件。

genmodel10

genmodel20

genmodel30

选择您的型号并按load。

genmodel40

genmodel50

12.具有Adapter和EContentAdapter的通知和适配器

EMF有可能将模型中的更改通知观察者/侦听者。可以侦听单个对象的更改以及集合中所有对象的更改。

为了测试这个通知,创建一个名为com.vogella.emf.notifications的新Java项目。

基于以下两个接口创建模型,并从中生成模型代码。

package com.vogella.emf.notifications.model;











import org.eclipse.emf.ecore.EObject;



/**

 * @model

 */

public interface IPerson extends EObject {

    /**

     * @model
     */

    public String getFirstName();

    void setFirstName(String value);
}
package com.vogella.emf.notifications.model;











import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;



/**
 * @model
 * @generated
 */
public interface IPersonList extends EObject {
    /**
     * @model containment="true"
     * @generated
     */
    EList<IPerson> getPersons();


} // IPersonList

创建以下两个类ElementObserver和TotalObserver。ElementObserver实例侦听列表的更改(删除、插入项),TotalObserver实例除了侦听列表中包含的元素的属性的更改外,还侦听。



package com.vogella.emf.notifications.observers;


import model.IPerson;
import model.IPersonList;
import model.ModelFactory;




import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;

public class ElementObserver {
    private IPersonList persons;


    public ElementObserver() {
        ModelFactory factory = ModelFactory.eINSTANCE;
        persons = factory.createIPersonList();


        Adapter adapter = new AdapterImpl() {
            public void notifyChanged(Notification notification) {
                System.out
                        .println("Notfication received from the data model. Data model has changed!!!");
            }
        };
        persons.eAdapters().add(adapter);
    }


    public void doStuff() {
        ModelFactory factory = ModelFactory.eINSTANCE;
        IPerson person = factory.createIPerson();
        person.setFirstName("Lars");
        System.out.println("I'm adding a person.");
        persons.getPersons().add(person);
        System.out.println("I'm changing a entry");
        persons.getPersons().get(0).setFirstName("Lars2");
    }

}
package com.vogella.emf.notifications.observers;










import model.IPerson;
import model.IPersonList;
import model.ModelFactory;



import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.util.EContentAdapter;


public class TotalObserver {
    private IPersonList persons;


    public TotalObserver() {
        ModelFactory factory = ModelFactory.eINSTANCE;
        persons = factory.createIPersonList();


        EContentAdapter adapter = new EContentAdapter() {
            public void notifyChanged(Notification notification) {
                super.notifyChanged(notification);
                System.out
                        .println("Notfication received from the data model. Data model has changed!!!");
            }
        };
        persons.eAdapters().add(adapter);
    }

    public void doStuff() {
        ModelFactory factory = ModelFactory.eINSTANCE;
        IPerson person = factory.createIPerson();
        person.setFirstName("Lars");
        System.out.println("I'm adding a person.");
        persons.getPersons().add(person);
        System.out.println("I'm changing a entry");
        IPerson person2 = persons.getPersons().get(0);
        person2.setFirstName("Lars2");

    }
}

创建一个主类来测试不同的行为并运行main方法。

package main;










import observers.ElementObserver;
import observers.TotalObserver;


public class Main {




    /**
     * @param args
     */
    public static void main(String[] args) {
        ElementObserver observ1 = new ElementObserver();
        observ1.doStuff();
        TotalObserver observ2 = new TotalObserver();
        observ2.doStuff();
    }
}

13.通过XMI持久化EMF模型

Eclipse建模框架(EMF)允许通过EMF持久性框架存储模型内容。EMF提供了XMI和XML持久性提供程序。默认情况下,EMF使用XMI(XML元数据交换)。XMI是通过可扩展标记语言(XML)交换元数据信息的标准。

下面演示如何创建EMF模型实例、保存它并再次加载它。

如果持久化EMF对象,则所有依赖对象都将自动持久化。没有包含关系的对象必须显式添加到resource.getContents().add()中。如果未添加对象且未包含在包含关系中,则在调用resource.save()方法时会引发异常。

14.示例

以下内容基于您创建的早期EMF模型。创建一个新的插件项目com.vogella.emf.webpage.instance。将以下依赖项添加到您的plugin.xml中。

  • org.eclipse.emf.ecore
  • org.eclipse.emf.ecore.xmi
package writeWebpage;












import java.io.IOException;

import java.util.Collections;

import java.util.Map;




import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;


import datamodel.website.MyWeb;
import datamodel.website.Webpage;
import datamodel.website.WebsiteFactory;
import datamodel.website.WebsitePackage;
import datamodel.website.impl.WebsitePackageImpl;


public class CreateSaveTester {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // Initialize the model
        WebsitePackage.eINSTANCE.eClass();
        // Retrieve the default factory singleton
        WebsiteFactory factory = WebsiteFactory.eINSTANCE;

        // create the content of the model via this program
        MyWeb myWeb = factory.createMyWeb();
        Webpage page = factory.createWebpage();
        page.setName("index");
        page.setDescription("Main webpage");
        page.setKeywords("Eclipse, EMF");
        page.setTitle("Eclipse EMF");
        myWeb.getPages().add(page);

        // As of here we preparing to save the model content


        // Register the XMI resource factory for the .website extension

        Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
        Map<String, Object> m = reg.getExtensionToFactoryMap();
        m.put("website", new XMIResourceFactoryImpl());

        // Obtain a new resource set
        ResourceSet resSet = new ResourceSetImpl();

        // create a resource
        Resource resource = resSet.createResource(URI
                .createURI("website/My2.website"));
        // Get the first model element and cast it to the right type, in my
        // example everything is hierarchical included in this first node
        resource.getContents().add(myWeb);

        // now save the content.
        try {
            resource.save(Collections.EMPTY_MAP);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

14.1加载现有模型

以下代码可用于加载现有模型。

package writeWebpage;












import java.util.Map;



import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;



import datamodel.website.MyWeb;
import datamodel.website.WebsitePackage;
import datamodel.website.impl.WebsitePackageImpl;


public class EMFModelLoad {
    public MyWeb load() {
        // Initialize the model
        WebsitePackage.eINSTANCE.eClass();

        // Register the XMI resource factory for the .website extension

        Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
        Map<String, Object> m = reg.getExtensionToFactoryMap();
        m.put("website", new XMIResourceFactoryImpl());

        // Obtain a new resource set
        ResourceSet resSet = new ResourceSetImpl();

        // Get the resource
        Resource resource = resSet.getResource(URI
                .createURI("website/My.website"), true);
        // Get the first model element and cast it to the right type, in my
        // example everything is hierarchical included in this first node
        MyWeb myWeb = (MyWeb) resource.getContents().get(0);
        return myWeb;
    }


}

然后,您可以通过标准Java编码访问模型内容。

package writeWebpage;












import java.util.Iterator;



import datamodel.website.MyWeb;
import datamodel.website.Webpage;




public class LoadTest {


    /**
     * @param args
     */
    public static void main(String[] args) {
        // Loading the existing model
        EMFModelLoad loader = new EMFModelLoad();
        MyWeb myWeb = loader.load();
        // Accessing the model information
        System.out.println(myWeb.getDescription());
        System.out.println(myWeb.getTitle());
        // Lets see what info the webpage has
        for (Iterator<Webpage> iterator = myWeb.getPages().iterator(); iterator
                .hasNext();) {
            Webpage page = iterator.next();
            System.out.println("Name : " + page.getName());
            // We could also iterate over the Articles...
        }
    }
}

15.附件:加密

EMF有可能在写入数据模型之前对其进行加密,并在加载之前对其解密。以下内容说明了这一点。

基于以下界面创建模型。



package mymodel;


import org.eclipse.emf.ecore.EObject;

/**

 * @model

 */

public interface IPerson extends EObject {

    /**

     * @model default="";
     */

    public String getLastname();
}

创建以下设置加密选项的工厂。

package factory;










import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.impl.AESCipherImpl;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;

public class MyXMIFactoryImpl extends XMIResourceFactoryImpl {



    @Override
    public Resource createResource(URI uri) {
        XMIResourceFactoryImpl resFactory = new XMIResourceFactoryImpl();
        XMIResource resource = (XMIResource) resFactory.createResource(uri);
        try {
            resource.getDefaultLoadOptions().put(Resource.OPTION_CIPHER,
                    new AESCipherImpl("12345"));
            resource.getDefaultSaveOptions().put(Resource.OPTION_CIPHER,
                    new AESCipherImpl("12345"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resource;
    }

}

创建以下测试类。

package load;










import java.io.IOException;

import java.util.Collections;

import java.util.Map;




import mymodel.IPerson;
import mymodel.MymodelFactory;
import mymodel.impl.MymodelPackageImpl;



import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;

import factory.MyXMIFactoryImpl;

public class Create {
    public void create() {
        MymodelPackageImpl.init();
        // Retrieve the default factory singleton
        MymodelFactory factory = MymodelFactory.eINSTANCE;

        // create the content of the model via this program
        IPerson person = factory.createIPerson();
        person.setLastname("Lars");


        // Register the XMI resource factory for the .website extension
        Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
        Map<String, Object> m = reg.getExtensionToFactoryMap();
        m.put("person", new MyXMIFactoryImpl());

        // Obtain a new resource set
        ResourceSet resSet = new ResourceSetImpl();

        // create a resource
        Resource resource = resSet.createResource(URI
        .createURI("mymodel.person"));
        resource.getContents().add(person);


        // now save the content.
            try {
                resource.save(Collections.EMPTY_MAP);
            } catch (IOException e) {
                e.printStackTrace();
            }
    }

    public void load() {
        // Initialize the model
        MymodelPackageImpl.init();
        // Register the XMI resource factory for the .website extension
        Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
        Map<String, Object> m = reg.getExtensionToFactoryMap();
        m.put("person", new MyXMIFactoryImpl());
        ResourceSet resSet = new ResourceSetImpl();
        Resource resource = resSet.getResource(URI
                .createURI("mymodel.person"), true);
        try {
            IPerson person= (IPerson) resource.getContents().get(0);
            System.out.println(person.getLastname());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        Create test = new Create();
        test.create();
        test.load();
    }
}

16.EMF资源

Eclipse EMF Homepage

EMF Documentation

Ecore translation

Eclipse ATL – allows model to model transformation for EMF

If you need more assistance we offer Online Training and Onsite training as well as consulting

See License for license information.

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYaZSvoO' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片