Eclipse EMF教程(下)

Eclipse EMF教程(下)

翻译自:https://eclipsesource.com/blogs/tutorials/emf-tutorial/
在接下来的部分中,我们将探索我们生成的代码的EMF API。

EMF API

在教程的这一部分,我们将探索EMF的API,包括生成的代码,以及EMF的实用程序类。让我们先看看生成的代码。

在我们的教程org.eclipse.example.bowling的模型插件中,您将找到所有模型实体的接口和实现。查看实体接口的轮廓可以发现,它包含我们在模型中定义的属性的getter和setters以及引用的getter。生成的EMF模型的所有实体都是EObject的子类。EObject包含基本功能–例如,更改通知机制。
在这里插入图片描述
模型插件包含创建模型元素实体的工厂。请注意,EObjects的构造函数通常不是公共的。还请注意,工厂被许多框架用于其功能,例如反序列化。成功地改变这些方法需要一些仔细的计划。让我们使用工厂以编程方式创建一些实体,并使用它们的API来修改它们。我们将使用预先生成的测试插件来运行这个示例代码。如果您打开插件org . eclipse . example . bowling model . test,您会发现为模型的所有实体生成了一个测试类。通过添加以“test”开头的方法,您可以创建单个测试用例。测试用例可以通过右击测试class =>“Debug As“=>“JUnit Test“来启动。请注意,我们不会真正“测试”我们的模型。在这种情况下,测试用例只是探索和使用生成类的API的一种非常简单的方式。

在这个非常简单的例子中,我们将使用BowlingFactory创建一场比赛和一场游戏,添加对比赛的引用并检查游戏的双向更新。

public void testMatchupGameRef() {Matchup matchup = BowlingFactory.eINSTANCE.createMatchup();Game game = BowlingFactory.eINSTANCE.createGame();matchup.getGames().add(game);assertEquals(game.getMatchup(), matchup);
}

超类EObjects提供了许多以更通用的方式访问实体的方法。例如,我们将通过访问EContainer而不是getMatchup()方法来测试Matchup和Game之间的包含关系。

public void testMatchupGameRef() {Matchup matchup = BowlingFactory.eINSTANCE.createMatchup();Game game = BowlingFactory.eINSTANCE.createGame();matchup.getGames().add(game);assertEquals(game.eContainer(), matchup);
}

EObjects使用方法eSet()和eGet()提供对其属性的反射访问。这在以通用方式修改实体时非常有用。

public void testReflection() {EObject eObject = BowlingFactory.eINSTANCE.createPlayer();eObject.eSet(BowlingPackage.eINSTANCE.getPlayer_Name(), "Jonas");Player player = (Player) eObject;assertEquals("Jonas", player.getName());
}

关于可用电子属性和电子引用的信息,以及我们之前建模的所有其他概念,都可以通过EClass或EPackage访问。以下测试检查联赛的EReference的重数是否大于1。

public void testReflectiveInformation() {League league = BowlingFactory.eINSTANCE.createLeague();assertTrue(league.eClass().getEAllReferences().get(0).isMany());assertTrue(BowlingPackage.eINSTANCE.getLeague_Players().isMany());
}

EMF还支持模型实例的验证。例如,我们可以验证模型的约束,即一场比赛必须总是由两场比赛组成。

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());
}

最后,EMF提供了许多实用程序类。一个非常重要的例子是EcoreUtil。浏览EcoreUtil的可用方法是值得的。我们将使用copy方法创建一个对象的副本。

public void testCopy() {Player player = BowlingFactory.eINSTANCE.createPlayer();player.setName("Jonas");Player copy = EcoreUtil.copy(player);assertNotSame(player, copy);assertEquals(player.getName(), copy.getName());
}

导入中间示例解决方案在我们继续本教程之前,请导入中间示例解决方案,该解决方案可以下载从我们的网站.

切换到一个空工作区(文件→切换工作区)并选择“导入”→“常规”→“将现有项目导入工作区”。选择“exampleSolution2.zip”并导入所有项目。

在这里插入图片描述

适配器工厂

对于本教程的下一部分,理解AdapterFactories的概念非常重要。我们将进行基本介绍。中还介绍了更高级的概念这篇博文.

AdapterFactories的基本功能是为您提供特定用途所需的接口,例如UI中所需的ILabelProvider。EMF为您生成了很多这样的类。要检索正确的类,可以使用所需接口的AdapterFactory实现,例如AdapterFactoryLabelProvider。AdapterFactoryLabelProvider将使用AdapterFactory为所有EObjects检索生成的LabelProvider。

在这里插入图片描述

EMF数据管理

在前面的章节中,我们已经展示了如何使用EMF生成结构化数据模型。在典型的应用程序中,必须存储这些数据模型,并且很可能还要对其进行版本控制和分发。有几个框架支持不同的用例。

默认情况下,EMF提供了将EObjects序列化为XMI文件的能力。在下面的例子中,我们将从一个文件中加载EObjects,然后保存它们。EMF还提供了修改模型的命令。命令很容易撤销。在本例中,我们将加载一个包含锦标赛的XMI文件。我们可以在锦标赛中添加新的比赛并撤销这些更改。完成后,我们可以将更改保存回文件。

对于本教程,我们在插件org . eclipse . example . bowling . tutorial中准备了一个示例对话框,该对话框已从示例解决方案中导入。您可以通过右键单击包含保龄球模型实例的文件并选择“教程”→“打开锦标赛示例对话框”来打开此对话框。在实现教程的下两个部分后,它将如下所示:
在这里插入图片描述
在子类ExampleTournamentDialog中,本教程中将实现空的方法存根。这里需要注意的是,在本教程中,我们关注的是简单而不是完美的设计。此外,与本教程无关的所有内容都在一个名为AbstractTournamentExampleDialog的抽象基类中实现。

现在您需要打开ExampleTournamentDialog类。我们将实现loadContent方法,该方法通过打开示例视图来触发。此方法的目的是从文件中获取锦标赛,然后在示例视图中显示。为了简单起见,我们假设文件包含一个锦标赛,这个锦标赛是文件中的第一个元素。您可以使用生成的示例编辑器轻松创建这样的文件。

首先,我们创建一个编辑域。编辑域管理一组相互关联的模型以及运行来修改它们的命令。例如,它包含所有以前命令的堆栈。编辑域可以创建一个资源,该资源是用于存储对象的容器。可以保存和加载资源,并向其中添加内容。在这个例子中,我们获得了资源中的第一个EObject,假设它是一个锦标赛(Tournament ),并使它成为我们超类的成员。

@Override
protected void loadContent(IFile file) throws IOException {// Load Tournament from file and set it with setTournamentAdapterFactoryEditingDomain domain = new AdapterFactoryEditingDomain(getAdapterFactory(),new BasicCommandStack());resource = domain.createResource(file.getFullPath().toString());resource.load(null);EObject eObject = resource.getContents().get(0);setTournament((Tournament) eObject);
}

加载内容后,我们将实现保存。这将通过在对话框中按OK来触发,并将序列化模型并将所有更改应用到文件。

@Override
protected void save() throws IOException {// save changes in the fileresource.save(null);
}

现在我们想在锦标赛中增加一场比赛。为此,我们将使用一个命令。首先,我们使用适当的工厂创建一场比赛。按照惯例,工厂与模型的基础包同名。然后我们创建一个命令,将新创建的比赛添加到在上一步中从资源加载的锦标赛中。最后,我们在编辑域的命令堆栈上运行命令。

@Override
protected void addMatchup() {// add a new Matchup using a CommandMatchup matchup = BowlingFactory.eINSTANCE.createMatchup();EditingDomain editingDomain = AdapterFactoryEditingDomain
.getEditingDomainFor(getTournament());Command command = AddCommand.create(editingDomain, getTournament(),BowlingPackage.eINSTANCE.getTournament_Matchups(),matchup);editingDomain.getCommandStack().execute(command);
}

此时,这些更改不会反映在对话框的用户界面中,但是我们将在教程的下一部分实现代码。

下一步是实现撤销。要撤消上一个命令,您只需在编辑域的命令堆栈上调用undo。

@Override
protected void undo() {// Undo the last changeAdapterFactoryEditingDomain.getEditingDomainFor(getTournament()).getCommandStack().undo();
}

现在,启动保龄球应用程序并使用示例编辑器创建一个XMI文件。它应该包含一场锦标赛和几场比赛。右键单击该文件并选择“教程”→“打开示例锦标赛视图”。在此视图中,您可以添加新的锦标赛、撤销此操作并点击“确定”进行保存。您可以通过在Ecore编辑器中打开文件来验证结果。请再次注意,视图的UI还没有更新,但是我们将在教程的下一步初始化UI。

EMF监听器

在本节中,我们将把标签绑定到模型的顶部,显示已开启锦标赛中的比赛数量。每当比赛数量发生变化时,我们将使用通知机制来更新标签。其次,我们将在TreeViewer中填充比赛列表,并显示他们作为孩子的游戏。为了更新标签,我们将在视图中打开的锦标赛对象上注册一个侦听器。如果锦标赛对象发生变化,EMF运行时将始终通知该监听器。

@Override
protected void initializeListener() {// initialize a listener for the Label displaying the number of MatchupsnumberOfMatchupListener = new NumberofMatchupListener();getTournament().eAdapters().add(numberOfMatchupListener);//添加监听器
}

第二步,我们将实现侦听器本身。在notify方法中,我们首先检查更改是否是对比赛的引用,并因此影响了比赛的数量。如果是这种情况,我们通过updateNumberOfMatchups方法(在AbstractTournamentExampleView中实现)更新标签。

private final class NumberofMatchupListener extends AdapterImpl {// Implement a listener to update the Label. Call updateNumberOfMatchups@Overridepublic void notifyChanged(Notification msg) {if (msg.getFeature() != null && msg.getFeature().equals(BowlingPackage.eINSTANCE.getTournament_Matchups())) {updateNumberOfMatchups();    }super.notifyChanged(msg);//通知监听器}
}

这就是手动实现侦听器的方式。为了在UI元素和数据模型之间创建双向更新的UI,我们建议使用EMF已经可用的数据绑定。在Eclipse数据绑定中,您可以将某个UI元素绑定到某个EAttribute或EReference,它将负责双向更新。

树查看器

接下来,我们将初始化TreeViewer以显示当前锦标赛的比赛及其子比赛。TreeViewer需要初始化三个东西:ContentProvider、LabelProvider和一个输入。ContentProvider通过提供getChildren()方法来定义树的结构。调用LabelProvider来获取要为一个节点显示的图标和文本。TreeViewer的输入是树的不可见根元素。显示在树根中的元素是该元素的子元素。在我们的例子中,输入是锦标赛。

在这里插入图片描述
ContentProvider尤其是LabelProvider通常依赖于某个EClass。EMF为多种目的生成提供者,包括内容提供者和标签提供者。我们将使用前面解释的AdapterFactory概念来检索每个元素的正确提供者。最后,我们设置当前打开的锦标赛的输入。

@Override
protected void initializeTreeviewer(TreeViewer treeViewer) {// initialize a TreeViewer to show the Matchups// and Games of the opened TournamentAdapterFactoryLabelProvider labelProvider =
new AdapterFactoryLabelProvider(
getAdapterFactory());AdapterFactoryContentProvider contentProvider =
new AdapterFactoryContentProvider(
getAdapterFactory());treeViewer.setLabelProvider(labelProvider);treeViewer.setContentProvider(contentProvider);treeViewer.setInput(getTournament());
}

为了测试刚刚实现的UI特性,您需要重新启动bowling示例应用程序。要修改标签的外观,只需修改相应类的ItemProvider。让我们为比赛修改LabelProvider。若要修改EObjects在TreeViewer中的外观,可以调整生成的item provider MatchupItemProvider。我们将在示例中显示一场比赛中包含的游戏数量。将该方法标记为“未生成”,以防止它在下一次生成时被覆盖。

/**
* 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");
}

在运行的应用程序中,新的LabelProvider显示在锦标赛示例视图和Ecore编辑器中:
在这里插入图片描述

最后一步,您应该在关闭视图时删除所有侦听器。请注意,LabelProvider和ContentProvider是模型上的注册侦听器,因此您也应该删除它们。

-----------------------------------------------------The End---------------------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/793573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言基础语法-教案16(从小白到劝退之结构体初阶)

最近给大家争取到一个 深夜福利 保证你在深夜手机刷到 嘎嘎香~ 那就是 大流量卡 缺点:月租太便宜 185GB~ 100分钟通话时长~ 长期套餐~ 畅想自由的气息 流量自由的同时还拥有超长通话,而且免费领取。 名额有限,咱们废话不多说直接上…

JS详解-手写Promise!!!

前言: 针对js的深入理解,作者学习并撰写以下文章,由于理解认知有限难免存在偏差,请大家指正!所有定义来自mdn。 Promise介绍: 对象表示异步操作最终的完成(或失败)以及其结果值. 描…

代码随想录训练营第三十五期|第2天|数组part02|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II ,总结

977. 有序数组的平方 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int[] sortedSquares(int[] nums) {int[] res new int[nums.length];int idx nums.length - 1;int left 0;int right nums.length - 1;while (left < right) {if (nums[left] * nu…

为什么JOS操作系统是用C语言实现的,why not高级编程语言?

C的优点 C提供了大量的控制能力&#xff0c;C可以完全控制内存分配与释放。C几乎没有隐藏的代码&#xff0c;几乎可以在阅读C代码的时候想象到对应的RISC-V机器指令是什么。通过C可以直接访问内存&#xff0c;可以读写PTE的bit位或者设备寄存器。使用C会有极少的依赖&#xff…

7天八股速记之Java 后端——Day 1

接口和抽象类的区别 接口抽象类方法抽象方法既可以有抽象方法&#xff0c;也可以有普通方法关键字修饰interfaceabstract定义常量变量只能定义静态常量成员变量子类方法所有方法必须实现实现所有的抽象方法子类继承多继承单继承构造方法不能有构造方法可以有构造方法接口实现只…

C++:逻辑运算符-非与或(19)

!非!a如果a为假&#xff0c;那么当前他就是真&#xff0c;如果a是真&#xff0c;那么他直接就是假&&与a&&ba与b都为真&#xff0c;那么就是真&#xff0c;如果两个里面有一个为假那么就是假||或a||ba或b有一个为真&#xff0c;那么就是真 非&#xff08;!&…

C++ templates: (3)、变量模板

1、普通变量模板 #include <iostream> using namespace std;template<typename T> int g_value 0;int main(){g_value<int> 10;g_value<double> 11;cout << g_value<int> << "," << g_value<double> &l…

【数据结构与算法】力扣 203. 移除链表元素

题目描述 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a; head [1,2,6,3,4,5,6], val 6 输出&#xff1a; [1,2,3,4,5]示例 2&#xff1a; 输…

一文弄懂CNN/RNN/GAN/Transformer等架构

1. 引言 本文旨在友好地介绍深度学习架构&#xff0c;包括卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#xff09;、生成对抗网络&#xff08;GAN&#xff09;、transformer 和 encoder-decoder 架构。 闲话少说&#xff0c;让我们直接开始吧。 …

【OpenCV】图像像素的遍历

1 前言 介绍两种遍历像素的方法&#xff08;非指针、指针&#xff09;。注意&#xff1a;.at() .ptr()的作用、用法。相关API&#xff1a; Mat对象.ptr() Mat对象.at() 2 代码及内容 #include "iostream" #include "opencv2/opencv.hpp"using namespac…

[已解决]OSError: Unable to load weights from pytorch checkpoint file

问题说明&#xff1a; 最近跑代码遇到了一个奇怪的问题&#xff1a; OSError: Unable to load weights from pytorch checkpoint file for /root/.cache/huggingface/transformers/c506559a5367a918bab46c39c79af91ab88846b49c8abd9d09e699ae067505c6.6365d436cc844f2f2b4885…

正则表达式(2)

文章目录 专栏导读1、贪婪与非贪婪2、转义匹配 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫实战教学》&#xff0c;本专栏针对大学生、初级数据分析工程师精…

国外服务器托管需要了解哪些信息

国外服务器托管服务提供了一种在国外租用并管理服务器的方式&#xff0c;适用于需要特定地域服务或对本地法规有特殊要求的企业和个人。那么想要进行国外服务器托管需要了解哪些信息呢?Rak部落小编为您整理发布国外服务器托管相关内容。 以下是一些关于国外服务器托管服务的详…

vue3表单参数校验+正则表达式

这里我们要实现在form表单中对表单项添加参数校验。 校验要求 我们的表单中有用户名、密码、电话号码、邮箱这四个项。 我们设置用户名为3到20位的非空字符 密码为3到25位非空字符 电话号码就用目前用的电话号码正则表达式&#xff0c;要求手机号码以 1 开头&#xff0c;第…

STM32单片机智能电表交流电压电流程序设计(电流 电压互感器TV1005M+TA1005M)

资料下载地址&#xff1a;STM32单片机智能电表交流电压电流程序设计(电流 电压互感器TV1005MTA1005M) 1、摘要 5、基于STM32F103单片机智能电表交流电压电流设计 本设计由STM32单片机核心板电路交流电压电流检测模块电路WIFI模块电路指示灯电路组成。 1、通过电压互感器TV100…

XML --java学习笔记

XML(全称EXtensible Markup Language&#xff0c;可扩展标记语言) 本质是一种数据的格式&#xff0c;可以用来存储复杂的数据结构&#xff0c;和数据关系 XML的特点 XML中的“<标签名>”称为一个标签或一个元素&#xff0c;一般是成对出现的XML中的标签名可以自己定义…

Doris实践——信贷系统日志分析场景的实践应用

目录 前言 一、早期架构演进 1.1 架构1.0 基于Kettle MySQL离线数仓 1.2 架构2.0 基于 Presto / Trino统一查询 二、基于Doris的新一代架构 三、新数仓架构搭建经验 3.1 并发查询加速 3.2 数仓底座建设 四、Doris助力信DolphinScheduler 和 Shell 贷业务场景落地 4.…

如何在 Ubuntu 14.04 上为 Nginx 添加 gzip 模块

简介 网站加载速度取决于浏览器需要下载的文件大小。减小传输文件的大小不仅可以加快网站加载速度&#xff0c;还可以减少需要支付带宽费用的用户的成本。 gzip 是一款流行的数据压缩程序。您可以配置 Nginx 使用 gzip 对其提供的文件进行实时压缩。这些文件在传输到浏览器时…

使用Python实现逻辑回归模型

逻辑回归是一种用于解决分类问题的统计学方法&#xff0c;尤其适用于二分类问题。在本文中&#xff0c;我们将使用Python来实现一个基本的逻辑回归模型&#xff0c;并介绍其原理和实现过程。 什么是逻辑回归&#xff1f; 逻辑回归是一种用于建立因变量与自变量之间关系的统计…

LabVIEW工程师在工作中需要特别注意哪些细节?

LabVIEW是一种图形编程语言&#xff0c;由美国国家仪器&#xff08;National Instruments&#xff0c;简称NI&#xff09;开发&#xff0c;广泛用于数据采集、仪器控制和工业自动化领域。LabVIEW工程师在工作中需要注意的细节颇多&#xff0c;既包括技术层面的&#xff0c;也包…