分享

使用 EMF 进行元建模:生成具体、可重用的 Java 代码片段

 gyb98 2010-12-11

EMF 是 Eclipse 平台的主要部分,并且是一些相关技术和框架的基础,比如 Eclipse Visual Editor、SDO、XSD 和 UML — 其中的许多技术都被集成到 Rational? Application Developer 和 WebSphere? Business Modeler 等 IBM? 平台中。现在,EMF 已经吸收了许多 Java 技术特性,比如枚举类型、注释和泛型。如果您还不熟悉 EMF,请参阅 参考资料 中的文章获得入门知识。

在大多数文档和教程中,EMF 都被用于建模数据接口(比如 EMF 发行文档中的 LibraryBooks),而不用于建模行为。 当然,还有一些针对数据对象生成的默认方法实现,但这些实现涉及到模型元素之间的关系。而且,将 EMF 用作 “元模型” 的经过归档的示例非常少 — 除了 Eclipse Foundation 文章 “Modeling Rule-Based Systems with EMF”(参见 参考资料)— 但是这个示例并没有展示如何扩展 Ecore 元模型。

最后,使用和扩展 EMF JET 模板的过程也没有被很好地进行归档。此外,JET Editor 项目最近已经迁移到另一个 Eclipse 项目(M2T)上。本文旨在澄清这些问题,并使您能够在 EMF 上下文中使用动态模板实现更多的功能。因此,本文假设您对 EMF 有基本的了解。

为什么要扩展 Ecore 元模型?

Ecore 究竟是什么?

Eclipse Modeling Framework (EMF) 是 Eclipse 的一个建模框架。根据 Eclipse Foundation 的定义,核心 EMF 框架包括一个描述模型的元模型(Ecore)和模型的运行时支持,包括更改通知、对默认 XMI 序列化的持久性支持和用于对 EMF 对象执行常规操作的反射 API(reflective API)。换句话说,Ecore 定义核心模型的结构,而核心模型定义开发人员用于维护应用程序数据的模型结构。

Ecore 元模型是一个强大的工具,可用于设计模型驱动架构(Model-Driven Architecture,MDA),后者可以作为软件开发的起点。通常情况下,我们定义应用程序范围内的对象(EClass 类型)、对象属性以及它们之间的关系。我们还使用 EOperation 模型元素定义属于这些对象的特定操作。默认情况下,EMF 将会为这些操作生成骨架 或方法签名,但是我们必须返回并实现这些操作,常常要反复地重新编写类似的逻辑。

但是,如果我们想在模型中指定某种任意的实现行为该怎么办呢?一种方法是添加基于文本的注释(EAnnotation 类型),以建模对象并在代码生成期间解释模板中的这些注释。关于这种方法的出色示例,可以查阅 Eclipse Foundation 文章 “Implementing Model Integrity in EMF with MDT OCL”(参见 参考资料)。但是,正如这篇文章中所描述的,我们的目标不是验证模型元素,而是对实现本身进行建模,以使任何具体的模型能够重用这些元模型元素。为此,我们需要扩展 Ecore 元模型。


扩展了的元模型

本文附带了一个高度简化的用来扩展 Ecore 的编程式模型。它不是一个完整或连贯的元模型或框架;严格来讲,它是一个元素的原型集合,用于演示使用 EMF 对代码实现进行元建模的能力。图 1 显示了我们的扩展元模型示例 EcoreX 的快照,下面是每个元素的简短描述。


图 1. EcoreX 模型
EcoreX 模型

EcoreX 元素

EPackageX 扩展 EPackage
这是 Ecore 元素 EPackage 的一个简单 “标记” 扩展,没有任何附加属性。这个元素是必需的,因为在默认情况下,元素 EPackage EMF 编辑器插件不允许将 EClass 的子类作为子元素添加(参阅下面的 EClassX)。通过提供一个可扩展 EPackage 的模型元素,代码将会自动生成,从而允许将一个 EClassX 子元素添加到 EPackageX 中。
EClassX 扩展 EClass
同样地,这是 Ecore 元素 EClass 的一个简单标记扩展,没有任何附加属性。与上面的元素类似,此元素也是必需的,因为在默认情况下,EClass 的编辑器插件不允许添加 EOperation 的子类 — 这正是我们要在本文中实现的目标。
EOperationImpl 扩展 EOperation
这是用于向 Ecore 模型添加具体的元功能的基本实体和入口点。此元素被赋予 Ecore 的基础 EOperation 元素中没有的属性。下面描述的所有其他元素都属于 EOperationImpl 并用于构成编程式实现。例如,EOperationImpl 包含变量和语句,可以返回一个引用或值。
LocalVariable 扩展 ETypedElement
LocalVariable 是一个本地变量。变量包含一个名称和一个 Java 类型(比如 StringIntegerObject),而且由于这些属性已经存在于其超级超类(super-superclass)EParameter 中,所以 LocalVariable 不需要额外属性。
Statement 扩展 EClass
在我们的简化逻辑模型中,一个 EOperationImpl 包含许多将会按给定顺序计算的 statementStatement 是一个抽象超类。
LiteralAssignment 扩展 Statement
LiteralAssignment 引用一个变量,并且有一个 String 属性,允许用户输入一个要被解析的值并将其分配给一个变量(例如,“hello”、“4.5” 可以分别分配给 Stringfloat)。
Access 扩展 Statement
Access 表示引用 Java 字段或操作的动作。
FieldReferenceAssignment 扩展 Access
访问一个字段,以分配一个值(例如,var1 = var2.name)。
Invoke 扩展 Access
调用一个操作(Java 方法)。Invoke 的结果可以分配给一个变量(例如,myVar = obj.toString())。

图 2 展示了 EcoreX 元模型的一种更加类似 UML 的表示。


图 2. Ecorex 模型图
Ecorex 模型图


入门

本文包括六个高级的步骤:

  1. 扩展 Ecore 元模型,添加新语义
  2. 为被扩展的元模型创建一个 genmodel
  3. 为此元模型生成一个 EMF 编辑器,并作为插件安装。
  4. 使用这个新编辑器,构建一个具体的模型来描述编程行为。
  5. 为这个具体的模型创建并配置一个 genmodel
  6. 基于这个具体的模型生成具体的 Java 代码。

可以创建或导入上面描述的元模型。两种情况都需要从一个现有 EMF 项目或创建一个新项目入手(New > Other > Eclipse Modeling Framework > Empty EMF Project)。我们的项目名为 EMFX,并且它应包含一个名为 model 的文件夹。可以将这个 EcoreX.ecore 模型(参见 参考资料)复制到 model 目录并跳至 构建和启动 Editor Metamodel 插件 小节,也可以执行以下步骤,从头创建一个元模型。

扩展 Ecore 元模型 — 从头开始

右键单击项目,从上下文菜单中选择 New > Other > Example EMF Model Creation Wizards > Ecore Model。(对于 Eclipse V3.5+ [Galileo, Helios],则应选择 New > Other > Eclipse Modeling Framework > Ecore Model。)选择 model 文件夹和名称 EcoreX.ecore

默认情况下,我们将模型包称为 ecorex。在模型窗口中右键单击并选择 Load Resource > Browse Registered Packages。选择具有名称空间 http://www./emf/2002/Ecore 的 Ecore Model。

导入 Ecore 元模型之后,就可以对其进行扩展了。要重新创建 ecorex.ecore 模型,首先在包元素 ecorex 上右键单击并选择 New Child EClass。将此元素称为 EPackageX(参阅上面的模型元素描述)。然后需要将基元素 EPackage 作为这个新元素的 ESuper Type 添加。

通过将 EClass 指定为 ESuperType,使用相同的过程创建新元素 EClassX。根据需要对 Ecore 对象划分子类,在 EcoreX 模型中继续定义其他 EClass。使用图 1 和 EcoreX.ecore 文件了解要为哪个 EClass 创建什么属性。

构建并启动 the Editor Metamodel 插件

I在构建步骤中,我们将创建元模型 genmodel 并构建模型和编辑器项目。右键单击 EcoreX 项目并选择 New > Other > Eclipse Modeling Framework > EMF Model。(对于 EMF V2.5+ [Galileo, Helios],则应选择 New > Other > Eclipse Modeling Framework > EMF Generator Model。)可以提供一个名称或接受默认的名称 EcoreX.genmodel。EcoreX 模型应该被预选择为 genmodel 的基模型。单击 Load 验证 EcoreX.ecore 元模型。


图 3. 新 EMF 模型
新 emf 模型

当要求指定要生成和从其他生成器模型引用的包时,选择 Root packages 下面的 EcoreX 包和 Referenced generator models 下面的 Ecore。

现在,向导将为元模型创建一个 genmodel。突出显示 genmodel 中的顶级元素之后,从上下文菜单中选择 Generate All,这样可以自动生成关联的代码。根据在 genmodel 中配置的行为,这将生成 4 个 Eclipse 项目。本文不会关注 .test 项目,所以您可能不希望生成这个插件。

现在我们继续启动步骤。在大多数 Eclipse 教程中,都会要求您在单独的 Eclipse 过程中启动所开发的插件。在本节中,我们将采用一种不同的方法:我们将在当前 Eclipse 和工作区中激活插件。这样更容易将预构建的元模型与下一节中具体的模型开发集成。为此:

  1. 双击 EMFX plugin.xml 打开插件配置编辑器。
  2. 单击 Exporting 选项卡下的 Export Wizard
  3. 选择基本的建模插件和两个编辑器插件。
  4. Destination 选项卡下,选择 Eclipse 安装目录,或托管存储库(如果可用)。

图 4. 导出
导出的方法

注意:如果使用 –console 选项启动 Eclipse,您可以使用 OSGi 命令控制台动态地启动、停止、更新和刷新插件(插件组),无需重新启动 Eclipse 或启动一个单独的实例。

单击 Finish 时,会自动构建生成的插件 JAR 文件,并自动将其复制到插件目录。此时,您需要重新启动 Eclipse,激活新插件。现在我们已经准备好启动编辑器插件了,创建一个新项目来保存我们的具体模型(我们的模型命名为 Test2)。

在这个新项目中,导航到 New > Other Example EMF Model Creation Wizards > Ecorex Model 并提供一个模型名称。注意:在 EMF 的最新版本 (V2.5+) 中, 具体模型的文件扩展名必须被设为 .ecore,而不是 .ecorex;否则,这个具体的 genmodel 将不能在后续步骤中被成功创建。选择 EPackageX 元素。您现在有了一个空的具体模型。后续小节将讨论如何构建这些编程模型元素;完成后的文件 My.ecore 可以在 参考资料 部分找到。


建模具体的测试模型

在本节中,我们将对一个具体的 Java 类(EClassX 的实例)进行建模,这个类包含两个具体的方法,我们将对这两个方法的实现进行建模。第一个示例方法接受 String 参数消息,并输出消息和一个时间戳 — 这有利于调试消息。以下是期望结果的表示。


清单 1. printTimestampMessage
            void printTimestampMessage(String message) {
            System.out.print(message);
            System.out.print("; Timestamp= ");
            System.out.println(System.currentTimeMillis());
            }
            

第二个示例接受 3 个基于日期的参数,并返回一个数字值,表示该日期对应的是星期几。


清单 2. getDayOfWeek
            int getDayOfWeek(int year, int month, int date) {
            int result;
            Calendar calendar = Calendar.getInstance();
            calendar.set(year, month, date);
            result = calendar.get(Calendar.DAY_OF_WEEK);
            return result;
            }
            

第一步是填入在上一节最后一步中创建的新 EPackageX 元素下的 3 个必需属性。如果在建模窗口下看不到 Properties 选项卡,可以从上下文菜单中选择 Show Properties View。在这个示例中,我们的包名为 mypackage。


图 5. EPackageX 属性
EPackageX 属性

接下来,向 mypackage 添加一个新 EClassX。可以在 mypackage 突出显示时使用上下文菜单完成此任务。填入 name 属性,为类提供一个名称(比如 MyClass),向新类添加两个 EOperationImpl 元素,并为它们指定方法名 printTimeStampMessagegetDayofWeek。然后,向每一个操作添加 Ecore 参数。


图 6. EOperationImpl getDayOfWeek()
EOperationImpl getdayofweek()


图 7. getDayOfWeek() 属性
getDayOfWeek() 属性

上面的操作 printTimestampMessage() 接受一个 EString 类型的参数,而 getDayOfWeek() 接受 3 个 EInt 类型的参数。此外,操作 getDayOfWeek 返回一个 EInt,这可以在 property 属性 EType 下进行配置(参见图 7)。

剖析 EOperationImpl

到现在为止,我们仅使用了继承的 Ecore 元素和属性。现在是时候使用我们扩展的元模型元素来构建 Java 实现了。

LocalVariable
查看一下图 8,printTimestampMessage() 将需要两个 LocalVariable 元素 — 一个为 EString 类型,另一个为 ELong 类型。

图 8. printTimestampMessage()
printTimestampMessage()


图 9. LiteralAssignment
LiteralAssignment

在图 9 中,Value 属性的字符串被内联到 LiteralAssignment。您可以设想一个不同的元模型,其中的文字值(常量)被建模为单独的元素。

接下来,我们插入一个 LiteralAssignment 类型的元素,它允许选择一个 LocalVariable 并为其分配值。在本例中,我们选择 String 变量并提供上面的原型方法中的文本值(记住在文本两边加上引号)。

DataType
再次查看上图,注意,有一个名为 SystemType 的 Ecore DataType,它是 java.lang.System 的一个包装器。必须将其添加到我们的 mypackage 包,因为它将会被随后的 Invoke 元素引用。
Statement
添加到这个操作的第一个 StatementSystemType 中的静态方法 currentTimeMillis() 的一个 Invoke,已经在上面定义了。

图 10. 调用 currentTimeMillis() 属性
调用 currenttimemillis() 属性

根据我们的元模型(我们将在下一节提供代码模板),上面的 Invoke 将转换为 Java 语句:timestamp = java.lang.System.currentTimeMillis();

下一个 Invoke 与之前的那个稍有不同。首先,没有 Assignment。其次,我们将把 message 参数的引用作为 Args 属性的一个参数。


图 11. 调用 out.print 属性
调用 out.print 属性

另外请注意,Access Name 属性的值为 out.print — 在这里我们实际上从 Java System 间接引用了字段 out,然后调用方法 print。我们使用了这种快捷方式,而没有结合使用一个 FieldReferenceAssignment 和一个 PrintStream 类型的 LocalVariable

操作中的第 3 个(最后一个)Invoke 是一个使用 LocalVariable timestamp 作为单个参数的 println()。这就完成了具体操作 printTimestampMessage() 的建模。

让我们看看第二个 EOperationImpl getDayOfWeek() 的完整模型。


图 12. getDayOfWeek()
getDayOfWeek()

DataTypes
在模型的底部,我们创建了一个额外的 DataType,名为 CalendarType,这是该操作所必需的。
LocalVariables
在操作模型的 3 个 LocalVariable 中,我们主要关注称为 resultLocalVariable,因为它将会保存执行完操作的最后一条语句之后返回的值。在 EOperationImpl 属性中有一个名为 Return Ref 的属性,而且在我们的实现中,我们使用下拉菜单选择 LocalVariable 结果。
Statement
正如图 12 所示,3 个 LocalVariable 之后是 3 个 Statement。第一个是 Invoke,它使用 CalendarType 元素上的 getInstance(),为 calendar 变量分配一个值,与图 10 中的操作类似。

接下来是对 calendar 变量执行的 set() 方法的 Invoke,现在它传递 3 个与 EOperationImpl 参数(yearmonthdate)相对应的 Arg


图 13. 带有参数的 set()
带有参数的 set()


图 14. FieldReferenceAssignment
FieldReferenceAssignment

根据我们的元模型,这个元素将会生成与 DAY = Calendar.DAY_OF_WEEK; 类似的 Java 代码。

在图 15 中,DAY 变量用于这个 EOperationImpl 的最后一个 Invoke:一个 get(),其返回值被分配给变量 result(我们的实现的 Return Ref


图 15. Return Ref
Return ref 

实现动态模板

我们现在设计了一个扩展的元模型,并用其描述了一个具体的模型 My.ecore(请参见上述的 EMF V2.5+ 文件名称说明)。现在终于可以用 JET 最终实现一些代码实现了。要查看 JET 模板的语法突出显示功能,您需要安装 JET Editor Plugin(参见 参考资料 和 “JET Editor 局限性”)。

默认情况下,在为模型生成代码时,EMF 不会使用动态模板。它使用预构建的 Java 类。要开始定制 JET 模板,我们需要从插件 JAR 文件 org.eclipse.emf.codegen.ecore_2.3.0.XYZ.jar 复制一些文件,其中 XYZ 是 Eclipse 插件文件夹中您的 EMF 版本的时间戳。本文使用 org.eclipse.emf.codegen.ecore_2.3.0.v200706262000.jar。要复制这些文件,请使用任意一种解压缩工具打开 JAR 文件,并执行以下操作:

  1. 从这个 JAR 文件将模板目录提取到您的具体模型的 Java 项目中。
  2. 在模板/模型中创建一个目录,名为 Class
  3. Class 文件夹中创建一个新的空文件,名为 implementedGenOperation.TODO.override.javajetinc 或从 参考资料 中复制。

由名称可以看出,第 3 步中的新文件是一个 JET 模板,我们将在其中加入模型对象 EOperationImpl 的代码生成逻辑。默认情况下,这个文件并不存在,因为 EMF 只为每个 EOperation 提供一个空的方法签名。一旦激活了动态模板功能,我们的新文件将被作为 Java 方法体自动包括,正如 EOperationImpl 所定义的。

以下是 implementedGenOperation.TODO.override.javajetinc 的完整代码。


清单 3. implementedGenOperation
            // created by implementedGenOperation.TODO.override.javajetinc
            <%
            if ( ! (genOperation.getEcoreOperation() instanceof EOperationImpl) ) { %>
            // TODO: implement this method
            // Ensure that you remove @generated or mark it @generated NOT
            throw
            new UnsupportedOperationException();
            <% } else { %>
            // ** EOperationX implementation **
            <% EOperationImpl opx = (EOperationImpl)genOperation.getEcoreOperation();
            Statement stm = null;
            Iterator iterator = null;
            EList<LocalVariable> pList = opx.getLocalVariables();
            LocalVariable lvar = null;
            String iname = null;
            StringBuffer paramsString = null;
            StringBuffer varString = null;
            for (int i = 0;i < pList.size(); i++) {
            lvar = pList.get(i);
            iname = lvar.getEType().getInstanceClassName();%>
            <%=iname%>
            <%=lvar.getName()%><%
            if (iname.startsWith("java")) { %> = null
            <% } %>;
            <% }
            iterator = opx.getStatements().iterator();
            while (iterator.hasNext()) {
            paramsString = new StringBuffer();
            varString = new StringBuffer();
            iname = null;
            stm = (Statement)iterator.next();
            if (stm instanceof LiteralAssignment) {%>
            <%= stm.getAssignment().getName()%> = <%=            ((LiteralAssignment)stm).getValue()%>;
            <%} else
            //
            if (stm instanceof FieldReferenceAssignment) {
            Access ax = (Access)stm;
            if (stm.getAssignment() != null) {
            varString.append(stm.getAssignment().getName());
            varString.append(" = ");
            }
            if ( ax.getStaticType() != null) {
            // STATIC
            iname = ax.getStaticType().getInstanceClassName();
            } else {
            // NON STATIC
            iname = ax.getTarget().getName();
            } %>
            <%=varString.toString()%><%=iname%>.<%=ax.getAccessName()%>;
            <% } else
            if (stm instanceof Invoke) {
            // INVOKE
            Invoke iv = ((Invoke)stm);
            if (stm.getAssignment() != null) {
            varString.append(stm.getAssignment().getName());
            varString.append(" = ");
            }
            for (int p = 0; p < iv.getArgs().size(); p++) {            paramsString.append(iv.getArgs().get(p).getName());
            if ( p + 1 < iv.getArgs().size() ) {
            paramsString.append(" , ");
            }
            }
            if (iv.getStaticType() != null) {
            // STATIC
            iname = iv.getStaticType().getInstanceClassName();
            } else {
            // NON STATIC
            iname = iv.getTarget().getName();
            } %>
            <%=varString.toString()%><%=iname%>.<%=iv.getAccessName()            %>(<%=paramsString.toString()%>);
            <% }
            } // STATEMENTS
            if (opx.getReturnRef() != null) { %>
            return
            <%=opx.getReturnRef().getName()%>;
            <% }
            } // EOPERATIONIMPL %>
            

对 JET 的详细讨论超出了本文的范围。但是,因为 JET 模板对我们的操作过程至关重要,我们将在伪代码方面回顾一下模板的内容。请记住,在处理模板之前,第一个变量 genOperation 已经被 Ecore/JET 预初始化。


清单 4. genOperation 被 Ecore/JET 预初始化
            Is this GenOperation is an EOperationImpl?
            If false, emit default UnsupportedOperationException
            STOP;
            Else, cast it to EOperationImpl;
            continue;
            Find and declare all elements of type LocalVariable, initializing Java Objects to null;
            Iterate through all Statements;
            Emit Java code according to the subtype;
            Does the implementation return something?
            If yes, emit the return statement;
            

在构建具体模型之前,需要执行一些操作。首先,在 templates/model/Class.javajet 顶部,我们必须将以下内容添加到导入列表(标记为粗体的前两行):

				<%@
            jet
            package="org.eclipse.emf.codegen.ecore.templates.model"
            imports="ecorex.*  org.eclipse.emf.common.util.*             java.util.* org.eclipse.emf.codegen.ecore.genmodel.*"…
            

当然,EcoreX 包是经过扩展的元模型。接下来,我们需要为我们的具体模型(My.ecore,类型为 '.ecorex')创建和配置一个 EMF(GenModel)。为此,在模型上右键单击并选择 New > Other > Eclipse Modeling Framework > EMF Model(对于 EMF V2.5+ [Galileo, Helios],应选择 New > Other > Eclipse Modeling Framework > EMF Generator Model。)创建完成之后,需要在属性组 Templates & Merge 下配置 3 个属性,在 Model 下配置第四个属性。


图 16. GenModel — Templates & Merge
GenModel

  1. Dynamic Templates 设置为 true。
  2. 指定 Template Directory
  3. 将 EMFX(扩展的元模型插件 ID)添加到 Template Plug-in Variables
  4. 最近版本:在 Model 组属性下,将 Suppress Interfaces 设置为 true

现在可以进行构建了,右键单击 GenModel 并选择 Generate Model Code。 如果一切顺利,在具体的 Test 项目(我们的项目称为 Test2)的源文件夹(src)中,您应该可以看到生成的 Java 源代码包和类,其中一个名为 mypackage.impl.MyClassImpl.java。打开该文件,您应该会看到两个生成的方法。


清单 5. MyClassImpl.java
            public
            void printTimestampMessage(String message) {
            // created by implementedGenOperation.TODO.override.javajetinc
            // ** EOperationX implementation **
            java.lang.String timestampStr = null;
            long timestamp;
            timestampStr = "; Timestamp = ";
            timestamp = java.lang.System.currentTimeMillis();
            java.lang.System.out.print(message);
            java.lang.System.out.print(timestampStr);
            java.lang.System.out.println(timestamp);
            }
            public
            int getDayOfWeek(int year, int month, int date) {
            // created by implementedGenOperation.TODO.override.javajetinc
            // ** EOperationX implementation **
            int result;
            int DAY;
            java.util.Calendar calendar = null;
            calendar = java.util.Calendar.getInstance();
            calendar.set(year , month , date);
            DAY = java.util.Calendar.DAY_OF_WEEK;
            result = calendar.get(DAY);
            return result;
            }
            

可以添加一个 main 方法测试这个类。

警告和故障诊断

Ecore 文件命名 (EMF V2.5+)

在 EMF V2.5 之前,正如上面的几个屏幕快照所示,从一个扩展了的 Ecore 模型生成的具体模型应该保留 '.ecorex' 的扩展名(如创建时的向导所建议的那样)。这有助于区别扩展了的模型与 ‘初级的’ Ecore 模型。然而,在 EMF 的最新版本中,genmodel 向导(如在图 16 之前所解释的)不接受除 .ecore 之外的其他文件扩展名。

JET Editor 局限性

要获得 JET 模板的语法突出显示功能,您需要安装 Eclipse JET Editor(JET Editor Plugin 最近已经从 EMF 迁移到 M2T)。

但是,在撰写本文时,JET Editor 的最新版本不能正确处理 Java 内容帮助或嵌套 JET 包含文件(比如 .javajetinc 文件)的动态编译。此外,为了确保构建成功,只能在父文件(比如上面的 Class.javajet)中指定导入操作,而不能在包含的文件中指定。

实际上,使用一些额外配置(即,使用项目的上下文菜单),您可以将 EMF 动态模板项目(本文示例中的 Test2)转换为 JET 项目。在实践中,上面提及的局限性以及 EMF 和 M2T/JET 之间缺少集成,使得这种方法不太可行。

因此,很难捕获和改正包含的模板文件中的错误。由于在生成最终代码之前,JET 模板首先会被编译为一种中间 Java 文件(默认情况下位于一个隐藏的 Java 项目 JETEmitter 中),所以通过从 Eclipse 的 Package Explorer 视图中删除过滤器,您能够看到这些编译错误。如果这只是模板文件中的格式错误,在构建期间将会出现一个 Eclipse 弹出窗口。或许在未来的 JET 版本中,我们会看到更多的改进功能。

未进行模型验证

本文中的示例未使用 EMF Validation Framework 或 OCL 功能。所以,模型中的不一致性将会导致构建失败,例如,一个 EOperationImpl 可以声明某种返回类型,但是 Return Ref 属性可以引用不同的类型或者为空。在构建模型期间,将不会发现这些错误,而且生成的代码将无法编译。可以对元模型进行改进,使用 OCL 增强完整性和约束(参见 参考资料)。

结束语

我们看到了如何扩展 Ecore 元模型,将合成的 Java 方法中的编程式行为概念化。通过导入 Ecore 本身,我们扩展了一些 Ecore 模型元素 — 尤其是 EOperation。然后构建了元模型,并使用编辑器设计了一个具体的测试模型,包括以 EOperationImpl 形式建模的两个 Java 方法。我们配置并构建了 JET 模板,用于为 EOperationImpl 生成代码。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多