emf java_每个Eclipse开发人员都应该了解EMF

This tutorial is an introduction to EMF and explains the basics of EMF. We start by showing you how to build a very simple data-centric application, including the UI, based on EMF. We explain how to define a model in EMF and generate code from it. We explore the API of the generated code, that is, how to create, navigate and modify model instances.

本教程是一个介绍EMF,并解释EMF的基本知识。首先,我们向您展示如何构建一个非常简单的以数据为中心的应用程序,包括基于EMF的用户界面。我们解释了如何在EMF中定义一个模型,并用它生成代码。我们探索生成代码的API,即:如何创建,操纵和修改模型实例。

Next we demonstrate how to build a UI based on this model using databinding. For our example, we build an application to manage a bowling league, including matches and players. Later on in the tutorial, we explore the advantages of using AdapterFactories and briefly look at data management in EMF. We also include a few pointers on the most important add-on technologies for EMF. If you are interested in getting fast results building an application based on EMF, maybe EMF Client Platform is also a good starting point for you, see this tutorial.

接下来我们将演示如何基于该模型使用数据绑定构建UI。比如,我们构建一个应用程序来管理一个保龄球联赛,包括比赛和球员。稍后在本教程中,我们将探讨使用AdapterFactories的优点并简单看看EMF数据管理。这还包括一些最重要的EMF插件技术点。如果你有兴趣获得基于EMF快速构建应用程序的结果,也许EMF客户机平台也是一个很好的起点,参见本教程。

PDF Download: This tutorial is also available for download as a PDF here.

PDF下载:本教程也可以下载PDF。

Installation Requirements: To work through the examples, you’ll need to download and install a fresh version of the Eclipse Modeling Tools from the Eclipse Download Page.

安装要求:通过例子,您需要从Eclipse下载页面下载并安装新版本的Eclipse建模工具。

Introduction

介绍

To answer the question, “What is EMF?”, we’ll borrow the description from the EMF website:

回答这个问题,“EMF是什么?”,我们借用EMF网站的描述:

“The EMF project is a modeling framework and code generation facility for building tools and other applications based on a structured data model. From a model specification described in XMI, EMF provides tools and runtime support to produce a set of Java classes for the model, along with a set of adapter classes that enable viewing and command-based editing of the model, and a basic editor.”

“EMF项目是一个基于一个结构化的数据模型为构建工具和其他应用程序的建模框架和代码生成工具。从XMI中定义的模型规范,EMF提供了使用模型产生一组Java类的工具和运行时支持,以及一组适配器类,支持可视化以及命令行的方式编辑模型,和一个基本编辑器”。

It is worth mentioning that in addition to being a successful modeling framework, EMF has also been a stable standard for many other modeling technologies. We recommend using EMF for any structured data model you want to create in Eclipse, especially if it is stored, displayed and modified in UIs.

值得一提的是,除了作为一个成功的建模框架,EMF也是许多其他稳定的建模技术标准。我们建议你在Eclipse中使用EMF创建任何结构化数据模型,尤其是如果它是存储,在ui上显示和修改。

The basic EMF workflow is very pragmatic; a model is created and defined in the Ecore format, which is basically a subset of UML Class diagrams. From an Ecore model, you can generate Java code.

基本的EMF工作流非常务实,在Ecore模型创建和定义格式,这基本上是UML类图的一个子集。从Ecore模型,您可以生成Java代码。

Later in this tutorial we will have two running instances of Eclipse. In the first instance, called the “IDE”, we will define the model and generate code from it. The second instance, called the “Runtime”, will be started from the IDE and will contain instances of the generated model.

稍后在本教程中我们将运行两个Eclipse实例。在第一种情况下,我们将使用IDE定义模型和生成代码。第二个实例,将使用IDE运行时启动包含实例生成的模型。

Need help?

Example Model

示例模型

In this tutorial we will create an example model for managing a bowling league and its tournaments. A League contains an arbitrary number of Players. A Tournament consists of an arbitrary number of Matchups. Each Matchup always contains two Games. A Game is a list of frames (the score) and is assigned to a certain Player. Finally, a Tournament has an Enumeration that determines the type of Tournament.

在本教程中,我们将创建一个示例模型来管理一个保龄球联盟和比赛。一个联盟包含任意数量的球员。比赛由任意数量的对局组成。每个对局总是包含两个游戏。每个游戏是一个列表的帧(分数)和分配的某个球员。最后,比赛有一个枚举,决定比赛的类型。

In the next section, we will show how to create and generate code from this model.

在下一节中,我们将展示如何从这个模型创建和生成代码。

Modeling

建模

We will create our example model in EMF to generate the entity classes for our application. The first step is to create an empty modeling project in your workspace. In your running IDE select “File” from the toolbar menu → “New” → “Other…” and choose “Empty EMF Project”.

我们将创建我们的示例模型EMF为应用程序生成实体类。第一步是在工作区中创建一个空的建模项目。在您的IDE运行,从工具栏选择“File”菜单→“New”→“Other…”并选择“Empty EMF Project”。

Click “Next”, enter a name for the project, e.g., “org.eclipse.example.bowlingmodel”, and hit “Finish”:

点击“下一步”,为项目输入一个名字,例如。”,org.eclipse.example。bowlingmodel”,点击“完成”:

The essential part of the modeling project is the model itself, defined in the format “Ecore”. Please right click the model folder in your new modeling project → “New” → “Other…” → “Ecore Model” → “Next” and give the ecore file the name “bowling.ecore”.

建模项目的重要部分是模型本身,“Ecore”中定义的格式。请在您的新模型建模项目右击文件夹 → “New” → “Other…” → “Ecore Model” → “Next”,给Ecore文件制定一个名称“bowling.ecore”。

Click “Finish” to create the model. It will open in the default Ecore editor, which allows the definition of Ecore models in a tree-based view. There are several additional options for defining Ecore models, including graphical modeling, textual modeling, Java annotations and importing from UML tools. We will stick to the default editor in this tutorial and later, briefly demonstrate the graphical editor for Ecore.

单击“完成”创建模型。它将在默认的Ecore编辑器打开,它允许在树型视图定义Ecore模型。有几个额外的选项来定义Ecore模型,包括图形建模、文本建模、Java注释和从UML工具导入。本教程中我们将使用默认编辑器,简要演示Ecore的图形编辑器。

In the Ecore editor tree, you can create and delete model elements as well as modify the structure of your model via drag and drop. Properties of model elements can be modified in a second view, which opens up on double-click or right-click → “Show Properties View”.

Ecore树编辑器,您可以通过拖拽模型的结构创建和删除以及修改模型元素。模型元素的属性可以在第二个视图中修改,打开在双击或右键→“显示属性视图”。

You’ll need to give thepackage of your new model a name and an URI. This will be done in the properties view. The URI is used to identify the model later on. Name the package “bowling”, set the Ns Prefix to “org.eclipse.example.bowling” and the Ns URI to ”http://org/eclipse/example/bowling”.

你需要给你的新模型的包一个名称和一个URI。这将是在properties视图中完成的。使用URI来识别模型。包的名字“bowling”,设置Ns前缀“org.eclipse.example.bowling”和Ns URI“http://org/eclipse/example/bowling”。

Now we can define our model elements as children of the root package. Create a new EClass by right clicking on the bowling package → “New Child” → “EClass” and set the name to Player in the properties view of the newly created EClass

现在我们可以定义模型元素作为根的子包。通过右键单击创建一个新的EClass bowling包→“New Child” → “EClass”和在新创建的EClass的属性视图设置球员名称。

From the context menu of an EClass, you can add EAttributes and EReferences as children. Create an EAttribute in the Player EClass and open the Property view for it. The properties of an EAttribute define its name, its data type and other properties, which we will cover later in the tutorial. Set the name to “name” and assign the EType “EString” (java.lang.string). Repeat this step and add a second EAttribute named “dateOfBirth” of type “EDate”. The convention we’ll use here is that all class names start with an uppercase letter, attributes and references start with a lowercase letter.

从EClass的上下文菜单,可以添加EAttributes和EReferences子元素。打开它的属性视图创建EClass为Player的EAttribute。这个EAttribute的属性定义它的name,它的数据类型和其他属性,我们将在本教程后面介绍。将名称设置为“name”,并分配EType为“EString”(java.lang.String)。重复此步骤,并添加一个名为“dateOfBirth”的“EDate”类型的第二个EAttribute。我们将在这里统一使用,所有的类名称以大写字母开头,属性和引用以小写字母开头。

EMF models usually build up a structured hierarchy, that is, model element instances. For example, a Player is contained in a specific container object. This provides a tree structure, which is useful for navigation and serialization (e.g. XML). This tree structure is often referred to as a containment tree. In our model, Players are contained in a League. It is important to note that this also implies that every Player is referenced by exactly one League, and thus cannot be part of more than one League. EMF will automatically make sure that a player is not contained in more than one league. If you add a player to a second league, its reference to the original league vanishes.

EMF模型通常建立成结构化层次,即,模型元素的实例。例如,一个球员被包含在一个特定的容器对象。这提供了一个树结构,这是点对于导航和序列(例如,XML)是有用的。这树结构通常被称为树容器。在我们的模型中,球员都包含在一个联赛。要注意的是,这也意味着,每个球员通过正好在一个联赛中引用,因此不能多于一个联赛是很重要的。EMF将自动确保一个球员没有在一个以上的联赛。如果添加一个球员到第二个联赛,其引用到原来的联赛将消失。

Create a second EClass and name it “League”. To identify the League, also create an EString attribute called “name”. The next step is to create an EReference between League and Player by right clicking on the League model element. Name the reference “players”. Set the EType of the reference to “Player”. Since a League can contain an arbitrary number of Players, set the upper bound to “-1”, the equivalent of “many”. Finally, set the property Containment to “true”, defining the EReference to be a containment reference.

创建第二个的EClass并命名为“League”。用来表示联赛,还创建一个名为“name”的EString属性。下一步是通过联盟模型元素上右击创建联赛和球员之间EReference。名称引用“players”。设置EType引用的类型为“Player”。由于联赛可以包含任意数量的球员,设置的上限为“-1”表示“many”。最后,设置Containment属性为“true”,定义它的EReference是一个containment引用。

We can already generate code from this first model iteration, which will be shown in the next section. EMF can also generate an example editor. With this editor you can create instances of the generated model, in our case, instances of Leagues and Players. This allows us to do initial testing on the model by creating instances of it. Then we can further refine and add more EAttributes and EReferences in a second iteration that will complete the model.

我们已经可以从这第一个模型迭代,并在下一节展示如何来生成代码。EMF还可以生成一个示例编辑器。有了这个编辑器,你可以创建生成的模型的实例,比如,联赛和球员的实例。这允许我们通过创建它的实例来做模型上的初步测试。然后,我们可以进一步细化,并在第二次迭代模型完成时增加更多的EAttributes和EReferences。

Need help?

Code Generation

代码生成

In this step, we will generate the entities from the Ecore file we have created. Note that if you need to change your model, you will be able to regenerate the entities again. EMF can deal with simple changes like adding model elements or EAttributes. If you have complex changes, like moving an attribute to another class, you will have to migrate existing instances of the model. This is supported by the EDAPT framework. (see http://www.eclipse.org/edapt/)

在这一步中,我们将从已经创建了的Ecore文件产生实体。请注意,如果你需要改变你的模型,你将能够再次重新生成实体。 EMF可以处理如添加模型元素或EAttributes简单的更改。如果你有复杂的变化,就像一个属性移动到另一个类,你将不得不迁移模型的现有实例。这是由EDAPT框架支持的。 (见http://www.eclipse.org/edapt/)

To generate entities, we first have to create a generator model. This allows you to configure properties for the code generation that are not part of the model itself. For example, source code is generated for the plugin and subfolder as well.

要生成实体,我们首先需要创建一个生成模型。这允许您配置的代码生成不是模型本身的一部分属性。例如,在该插件和子文件夹中生成源代码。

Right click the model folder in the project → “New” → “Other…” → “EMF Generator Model” → “Next” and enter bowling.genmodel as the file name. Proceed to the next page and select “Ecore model” as the model importer. After clicking “Next”, select “Browse Workspace…” and select our previously created bowling.ecore. Go the next wizard page and select “Finish”.

右键单击该模型文件夹中的项目→ → “New” → “Other…” → “EMF Generator Model” → “Next” ,输入bowling.genmodel作为文件名。进入下一个页面,选择 “Ecore model”作为模型导入。点击“Next”后,选择“浏览工作空间...”并选择我们先前创建bowling.ecore。进入下一个向导页,并选择“Finish”。

In the root node of the generator model, you can set the properties for generating code. In the tree of the generator model, we can set properties for every generated entity. For the first code generation, we’ll use the default settings. Based on the generator model, we can now generate the source code. EMF allows you to generate a maximum of four different plugins for a defined model:

在生成模型的根节点,您可以设置生成代码的属性。在生成模型的树上,我们可以为每个生成的实体设置属性。对于第一次代码生成,我们将使用默认设置。基于生成模型,我们现在可以生成源代码了。 EMF允许你在定义的模型上生成最多四个不同的插件:Model: The model contains all entities, packages and factories to create instances of the model.

Edit: The edit plugin contains providers to display a model in a UI. For example, the providers offer a label for every model element, which can be used to display an entity showing an icon and a name.

Editor: The editor plugin is a generated example editor to create and modify instances of a model.

Test: The test plugin contains templates to write tests for a model.

To generate the plugins, right-click on the root node of the generator model and select the plugin. For our tutorial, please select “generate all”.

Before we look at the generated code, let’s start the application and create an entity of our model. Right click on the plugin containing the ecore file and select “Debug as → Eclipse Application”. This will start a new runtime Eclipse.

Then, in the runtime, create a new empty project (Toolbar menu → “File” → “New” → ”Other…” → “General” → “Project”) named bowlinginstance.

Right click the created project → “New” → “Other…” → “Example EMF Model Creation Wizards” → “Bowling Model” → “Next” and enter league.bowling as the name. This file will contain a serialized version of our model instance.

Select League as the model object. This sets the root object of the model instance we are about to create.

The generated editor for model instances works similarly to the Ecore editor. Model element instances can be created via a right-click and EAttributes can be modified in the properties view. Please give the League a name and create two Players. On save, all created instances are serialized in the XMI file “league.bowling”.

Need help?

Model Refinement

模型优化

Let’s switch back to our IDE Eclipse environment, complete the model and regenerate the source code. In this second model iteration, we will add different type of EReferences as well as EEnums and Multi-EAttributes. First, add the following classes to the bowling model:Tournament

Matchup

Game

These classes model the results of bowling tournaments and build up a second tree in our model. Therefore, we add containment EReferences from Tournament to Matchup and from Matchup to Game. Remember to set the containment property to true for both references. Following the bowling rules, a Matchup consists of two Games (each from one Player). We model this by setting the upper bound and lower bound of the EReference “games” of the EClass Matchup to “2”.

We defined the EReference between Matchup and Game as bi-directional. This means that the reference can be navigated from both ends. Therefore we have to create a second EReference from Game to Matchup and bind both EReferences. EMF will take care of the bi-directional synchronization. In other words, adding a Matchup to a Game will automatically add the Game to the Matchup.

Please add an EReference to Game called “matchup” with the EType “Matchup”. By setting the EOpposite to the EReference “games”, both EReferences are coupled bi-directionally. Note that the property “Container” will automatically be set to True.

The next step is to add a cross-EReference. In contrast to containment EReferences, cross-referenced model elements do not contain each other. In our model, we add a cross-referencing EReference from Game to Player named “player”. Set both container and containment properties to “false”. An arbitrary number of games can be assigned to a Player now and the Player is still contained in a League.

As a final mandatory step, we will create an EEnumeration for the type of Tournament. A Tournament can be of type “Pro” and “Amateur” in our model. Please create an EEnum by right-clicking on the root bowling model package, in the same way we created a class. Add two EEnum Literals to this EEnum.

Then, add an EAttribute to the EClass Tournament, name it “type” and set the EType to “TournamentType”.

The extended example model contains more EAttributes and EReferences to be added including all basic types and some special cases as the Multi-Integer EAttribute in Tournament. If you’d like, you can also model the following features:

Playerheight: EDouble

isProfessional: EBoolean

Gameframes: EInt, UpperBound = 10

After applying complex changes to the model it is always a good idea to validate it with a right-click on the model root in the Ecore editor. Let’s do something wrong in the model and set the lower bound of the EAttribute “games” (in Matchup) to 3. As the upper bound is 2, this model doesn’t make too much sense. This will be detected by the model validation – something that is impossible in plain Java code.

After this model refinement, we will re-generate the code to reflect our changes. Start the runtime Application again and create a second model “tournament”. Add a Matchup and two Games. To assign the Games to Players you will have to load the “league” model created earlier. Select “Load Resource” from the menu “Bowling Editor” and select the first model file. Now link the Games to the Players in the properties view.

Why is This Better than Writing POJOs?

You might ask, “Why should I use EMF instead of creating the model by writing plain POJOs?” Without considering benefits like the generated editor for rapid testing and all the additional frameworks available for EMF, let’s look at two very simple and exemplary benefits.

Before we look at the generated code (we will do that in a minute), let’s consider the amount of code we have just produced. The Eclipse metrics plugin tells us that we have generated over 1,000 LOC, while only 150 are part of utility classes. Even very simple code is considered to be worth $1 per LOC. So, we have earned $1,000 just by clicking some buttons ?

In the next sections, we’ll explore the EMF API for the code we have generated.

Need help?

EMF API

In this part of the tutorial, we will explore EMF’s API, including the generated code, as well as EMF’s utility classes. Let’s have a look at the generated code first.

In the model plugin from our tutorial org.eclipse.example.bowling you will find interfaces and implementations for all of the model’s entities. A look at the outline of an entity’s interface reveals that it contains getters and setters for the attributes we have defined in the model as well as getters for the references. All entities of the generated EMF model are subclasses of EObject. EObject contains basic functionality – for example, a change notification mechanism.

The model plugin contains factories to create model element entities. Note that the constructor of EObjects is usually not public. Please also note that factories are used by many frameworks for their functionality, e.g., deserialization. Changing these methods successfully requires some careful planning. Let’s use the factories to programmatically create some entities and use their APIs to modify them. We will use the pre-generated test plugin to run this example code. If you open the plugin, org.eclipse.example.bowlingmodel.test, you will find a generated test class for all of the entities of your model. By adding methods starting with “test” you can create single test cases. The test cases can be started with a right-click on the test class => “Debug As” => “JUnit Test”. Please note that we will not really “test” our model. In this context test cases are just a very simple way of exploring and using the API of the generated classes.

In this very simple example, we’ll use the BowlingFactory to create a Matchup and a Game, adding a reference to the Matchup and checking the bi-directional update on the Game.

public void testMatchupGameRef() {

Matchup matchup = BowlingFactory.eINSTANCE.createMatchup();

Game game = BowlingFactory.eINSTANCE.createGame();

matchup.getGames().add(game);

assertEquals(game.getMatchup(), matchup);

}

The super class EObjects offers many methods to access an entity in a more generic way. For example, we will test the containment between Matchup and Game by accessing the EContainer instead of the getMatchup() method.

public void testMatchupGameRef() {

Matchup matchup = BowlingFactory.eINSTANCE.createMatchup();

Game game = BowlingFactory.eINSTANCE.createGame();

matchup.getGames().add(game);

assertEquals(game.eContainer(), matchup);

}

EObjects offer reflective access to their attributes using the methods eSet() and eGet(). This can be useful in modifying an entity in a generic way.

public void testReflection() {

EObject eObject = BowlingFactory.eINSTANCE.createPlayer();

eObject.eSet(BowlingPackage.eINSTANCE.getPlayer_Name(), “Jonas”);

Player player = (Player) eObject;

assertEquals(“Jonas”, player.getName());

}

Information about the available EAttributes and EReferences, as well as all additional concepts we have modeled before, can be accessed either through the EClass or through the EPackage. The following test checks whether the EReference of League is a multiplicity greater than one.

public void testReflectiveInformation() {

League league = BowlingFactory.eINSTANCE.createLeague();

assertTrue(league.eClass().getEAllReferences().get(0).isMany());

assertTrue(BowlingPackage.eINSTANCE.getLeague_Players().isMany());

}

EMF also supports the validation of model instances. For example, we can validate the model’s constraint that a matchup must always consist of two games.

public void testValidation() {

Matchup matchup = BowlingFactory.eINSTANCE.createMatchup();

matchup.getGames().add(BowlingFactory.eINSTANCE.createGame());

Diagnostic validate = Diagnostician.INSTANCE.validate(matchup);

assertEquals(Diagnostic.ERROR, validate.getSeverity());

}

Finally, EMF provides many utility classes. A very important one is EcoreUtil. It is worthwhile browsing through the available methods of EcoreUtil. We’ll use the copy method to create a copy of an EObject.

public void testCopy() {

Player player = BowlingFactory.eINSTANCE.createPlayer();

player.setName(“Jonas”);

Player copy = EcoreUtil.copy(player);

assertNotSame(player, copy);

assertEquals(player.getName(), copy.getName());

}

Import Intermediate Sample Solution

Before we continue with the tutorial, please import the intermediate sample solution, which can be downloaded here.

Switch to an empty workspace (File → Switch Workspace) and select “Import” → “General” → “Existing Projects into Workspace”. Select “EMFTutorialIntermediateSolution.zip” and import all projects.

AdapterFactories

For the next sections of the tutorial, it is important to understand the concept of AdapterFactories. We will give a basic introduction. More advanced concepts are also described here.

The basic function of AdapterFactories is to provide you with the interface you need for a certain purpose such as an ILabelProvider needed in the UI. EMF generates a lot of these classes for you. To retrieve the right class, you can use an AdapterFactory implementation of the interface you need, e.g., an AdapterFactoryLabelProvider. The AdapterFactoryLabelProvider will retrieve the generated LabelProviders for all EObjects using an AdapterFactory.

EMF Data Management

In the previous sections we have shown how to generate a structured data model with EMF. In a typical application, these data models have to be stored and likely also versioned and distributed. There are a couple of frameworks that support different use cases.

By default EMF provides the ability to serialize EObjects to XMI files. In the following example, we will load EObjects from a file and save them afterwards. EMF also offers commands to make modifications to the model. Commands can be easily undone. In the example, we will load an XMI file containing a Tournament. We can add new Matchups to that Tournament and undo these changes. When we’re finished, we can save the changes back to the file.

For the tutorial, we have prepared an example dialog in the plugin org.eclipse.example.bowling.tutorial, which has been imported from the sample solution. You can open this dialog by right-clicking a file containing instances of a bowling model and select “Tutorial” → ”Open Tournament Example Dialog”. After implementing the next two sections of the tutorial, it will look like this:

In the subclass ExampleTournamentDialog, there are empty method stubs to be implemented in this tutorial. Just a note here that for the purposes of the tutorial we have focused on simplicity over perfect design. Also, everything that is not relevant for the tutorial is implemented in an abstract base class called AbstractTournamentExampleDialog.

Now you’ll need to open the class ExampleTournamentDialog. We will implement the loadContent method, which is triggered by opening the example view. The purpose of this method is to get a Tournament from the file that is then displayed in the example view. To keep it simple, we assume that the file contains a Tournament and this Tournament is the first element in the file. You can easily create a file like this with the generated example editor.

First, we create an editing domain. An editing domain manages a set of interrelated models and the commands that are run to modify them. For example, it contains the stack of all former commands. An editing domain can create a resource, which is a container for storing EObjects. Resources can be saved and loaded and contents can be added to them. In the example, we get the first EObject in the resource, assume it is a Tournament and make it a member of our superclass.

@Override

protected void loadContent(IFile file) throws IOException {

// Load Tournament from file and set it with setTournament

AdapterFactoryEditingDomain domain = new AdapterFactoryEditingDomain(

getAdapterFactory(),

new BasicCommandStack());

resource = domain.createResource(file.getFullPath().toString());

resource.load(null);

EObject eObject = resource.getContents().get(0);

setTournament((Tournament) eObject);

}

After loading the content, we will implement a save. This will be triggered by pressing OK in the dialog and will serialize the model and apply all changes to the file.

@Override

protected void save() throws IOException {

// save changes in the file

resource.save(null);

}

Now we want to implement the addition of a Matchup to a Tournament. We will use a command for this. First we create a Matchup using the appropriate factory. The factory, by convention, has the same name as the base package of the model. Then we create a command which adds the newly created Matchup to the Tournament that has been loaded from the resource in the previous step. Finally, we run the command on the command stack of the editing domain.

@Override

protected void addMatchup() {

// add a new Matchup using a Command

Matchup matchup = BowlingFactory.eINSTANCE.createMatchup();

EditingDomain editingDomain = AdapterFactoryEditingDomain

.getEditingDomainFor(getTournament());

Command command = AddCommand.create(editingDomain, getTournament(),

BowlingPackage.eINSTANCE.getTournament_Matchups(),

matchup);

editingDomain.getCommandStack().execute(command);

}

At this point, the changes will not be reflected in the dialog’s UI, but we will implement the code in the next section of the tutorial.

The next step is to implement undo. To undo the last command, all you’ll need to do is to call undo on the command stack of the editing domain.

@Override

protected void undo() {

// Undo the last change

AdapterFactoryEditingDomain

.getEditingDomainFor(getTournament())

.getCommandStack().undo();

}

Now, start the bowling application and create an XMI file with the example editor. It should contain a Tournament and several Matchups and Games. Right click on the file and select “Tutorial” → ”Open Example Tournament View”. In this view, you can add new Tournaments, undo this operation and save by clicking on “OK”. You can validate the result by opening the file in the Ecore editor. Please note again that the UI of the View will not be updated yet, but we will initialize the UI in the next step of the tutorial.

Need help?

Additional Persistence Frameworks

There are several frameworks for storing and versioning EMF model instances. Here are three that we can recommend:EMFStore (Model Repository)

CDO (Model Repository)

Teneo (Database Back-end)

EMFStore Merge Dialog

EMF UI

In this section we will show two examples of how EMF supports in developing UIs and thereby fill the example view with two basic UI elements. More concretely, we will show, how to attach listeners to EMF model instances and how to create a tree viewer based on EMF. Please note, this is only the tip of the iceberg. EMF offers extensive support for creating different kinds of UIs based on a given data model. As an example, it supports databinding to bind UI elements to the data of a model instance. Furthermore, there are several frameworks supporting UI development, which are summarized at the end of this section. For instance, if you want to create a form-based UI allowing you to show and enter attributes and references from your data model, such as shown below, you should take a look at EMF Forms.

EMF Listener

In this section, we will bind the Label to the top showing the number of Matchups in the opened Tournament to the model. We will use the notification mechanism to update the Label whenever the number of Matchups changes. Second, we will fill the TreeViewer with a list of the Matchups and display their Games as children. To update, the Label we will register a listener on the Tournament EObject, which is opened in the view. This listener will always be notified by the EMF runtime if there is a change in the Tournament EObject.

@Override

protected void initializeListener() {

// initialize a listener for the Label displaying the number of Matchups

numberOfMatchupListener = new NumberofMatchupListener();

getTournament().eAdapters().add(numberOfMatchupListener);

}

In the second step, we will implement the listener itself. In the notify method, we check first whether the change was on the EReference to Matchups and consequently influenced the number of Matchups. If this is the case, we update the Label via the updateNumberOfMatchups method (implemented in the AbstractTournamentExampleView).

private final class NumberofMatchupListener extends AdapterImpl {

// Implement a listener to update the Label. Call updateNumberOfMatchups

@Override

public void notifyChanged(Notification msg) {

if (msg.getFeature().equals(

BowlingPackage.eINSTANCE.getTournament_Matchups())) {

updateNumberOfMatchups();

}

super.notifyChanged(msg);

}

}

This is how you would manually implement listeners. To create UIs with bi-directional updates between UI elements and data-models, we recommend using data binding that is already available for EMF. In Eclipse data-binding, you can bind a certain UI element to a certain EAttribute or EReference, and it will take care of bi-directional updates.

To implement form-based UIs, you should also have a look at EMF Forms.

Tree Viewer

Next, we will initialize the TreeViewer to display the Matchups of the current Tournament and their Games as children. A TreeViewer needs three things to be initialized: the ContentProvider, the LabelProvider and an input. The ContentProvider defines the structure of the Tree by providing the method getChildren(). The LabelProvider is called to get an icon and the text to be displayed for one node. The input of a TreeViewer is the invisible root element of the Tree. The elements displayed in the root of the tree are the children of that element. In our case, the input is the Tournament.

ContentProvider and especially LabelProvider usually depend on a certain EClass. EMF generates providers for several purposes including Content- and LabelProvider. We will use the AdapterFactory concept explained previously to retrieve the right provider for every element. Finally, we set the Input to the Tournament that is currently open.

@Override

protected void initializeTreeviewer(TreeViewer treeViewer) {

// initialize a TreeViewer to show the Matchups

// and Games of the opened Tournament

AdapterFactoryLabelProvider labelProvider =

new AdapterFactoryLabelProvider(

getAdapterFactory());

AdapterFactoryContentProvider contentProvider =

new AdapterFactoryContentProvider(

getAdapterFactory());

treeViewer.setLabelProvider(labelProvider);

treeViewer.setContentProvider(contentProvider);

treeViewer.setInput(getTournament());

}

To test the UI features just implemented, you’ll need to re-start the bowling example application. To modify the appearance of a Label you can simply modify the ItemProvider of the respective class. Let’s modify the LabelProvider for Matchup. To modify the appearance of EObjects in the TreeViewer, you can adapt the generated ItemProvider MatchupItemProvider. We will show the number of Games contained in a Matchup Game in the example. Mark the method as “generated NOT” to prevent it from being overwritten during the next generation.

/**

* This returns the label text for the adapted class.

*

* –>

* @generated NOT

*/

@Override

public String getText(Object object) {

if (object instanceof Matchup) {

EList games = ((Matchup) object).getGames();

if (games != null) {

return “Matchup, Games: ” + games.size();

}

}

return getString(“_UI_Matchup_type”);

}

In the running application, the new LabelProvider is displayed in the Tournament Example view as well as in the Ecore Editor:

As a last step, you should remove all listeners when closing the view. Note that LabelProvider and ContentProvider are registered listeners on the model, so you should delete them as well.

If you want to have a look at the final sample solution, please import the projects from this link.

Additional UI Frameworks

There are several frameworks for displaying data from an EMF model instance. If you want to create form-based UIs, maybe even for different UI platforms, such as in the screenshot below, you should definitely have a look at EMF Forms.

If you want to create an application similar to the screenshot below, you should definitely have a look at the EMF Client Platform (Tutorial).

EMF Client Platform Navigator and Editor

Additional frameworks that are worthwhile having a look at for creating UIs are:

Additional EMF-based Technologies

In this last section of the tutorial we’d like to give you our short list of additional EMF-based technologies for you to explore:EMF Compare for comparing models

EDAPT for migrating models

EMF IncQuery for querying models

XText for textual modeling (DSLs)

Conclusion

We hope you found this tutorial helpful. If you have feedback or questions, please feel free to contact us at modeling@eclipsesource.com. Updates of this tutorial will become available here.

Need help?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值