日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

使用 EMF 進(jìn)行元建模:生成具體、可重用的 Java 代碼片段

 gyb98 2010-12-11

EMF 是 Eclipse 平臺的主要部分,并且是一些相關(guān)技術(shù)和框架的基礎(chǔ),比如 Eclipse Visual Editor、SDO、XSD 和 UML — 其中的許多技術(shù)都被集成到 Rational? Application Developer 和 WebSphere? Business Modeler 等 IBM? 平臺中?,F(xiàn)在,EMF 已經(jīng)吸收了許多 Java 技術(shù)特性,比如枚舉類型、注釋和泛型。如果您還不熟悉 EMF,請參閱 參考資料 中的文章獲得入門知識。

在大多數(shù)文檔和教程中,EMF 都被用于建模數(shù)據(jù)接口(比如 EMF 發(fā)行文檔中的 LibraryBooks),而不用于建模行為。 當(dāng)然,還有一些針對數(shù)據(jù)對象生成的默認(rèn)方法實(shí)現(xiàn),但這些實(shí)現(xiàn)涉及到模型元素之間的關(guān)系。而且,將 EMF 用作 “元模型” 的經(jīng)過歸檔的示例非常少 — 除了 Eclipse Foundation 文章 “Modeling Rule-Based Systems with EMF”(參見 參考資料)— 但是這個示例并沒有展示如何擴(kuò)展 Ecore 元模型。

最后,使用和擴(kuò)展 EMF JET 模板的過程也沒有被很好地進(jìn)行歸檔。此外,JET Editor 項(xiàng)目最近已經(jīng)遷移到另一個 Eclipse 項(xiàng)目(M2T)上。本文旨在澄清這些問題,并使您能夠在 EMF 上下文中使用動態(tài)模板實(shí)現(xiàn)更多的功能。因此,本文假設(shè)您對 EMF 有基本的了解。

為什么要擴(kuò)展 Ecore 元模型?

Ecore 究竟是什么?

Eclipse Modeling Framework (EMF) 是 Eclipse 的一個建模框架。根據(jù) Eclipse Foundation 的定義,核心 EMF 框架包括一個描述模型的元模型(Ecore)和模型的運(yùn)行時(shí)支持,包括更改通知、對默認(rèn) XMI 序列化的持久性支持和用于對 EMF 對象執(zhí)行常規(guī)操作的反射 API(reflective API)。換句話說,Ecore 定義核心模型的結(jié)構(gòu),而核心模型定義開發(fā)人員用于維護(hù)應(yīng)用程序數(shù)據(jù)的模型結(jié)構(gòu)。

Ecore 元模型是一個強(qiáng)大的工具,可用于設(shè)計(jì)模型驅(qū)動架構(gòu)(Model-Driven Architecture,MDA),后者可以作為軟件開發(fā)的起點(diǎn)。通常情況下,我們定義應(yīng)用程序范圍內(nèi)的對象(EClass 類型)、對象屬性以及它們之間的關(guān)系。我們還使用 EOperation 模型元素定義屬于這些對象的特定操作。默認(rèn)情況下,EMF 將會為這些操作生成骨架 或方法簽名,但是我們必須返回并實(shí)現(xiàn)這些操作,常常要反復(fù)地重新編寫類似的邏輯。

但是,如果我們想在模型中指定某種任意的實(shí)現(xiàn)行為該怎么辦呢?一種方法是添加基于文本的注釋(EAnnotation 類型),以建模對象并在代碼生成期間解釋模板中的這些注釋。關(guān)于這種方法的出色示例,可以查閱 Eclipse Foundation 文章 “Implementing Model Integrity in EMF with MDT OCL”(參見 參考資料)。但是,正如這篇文章中所描述的,我們的目標(biāo)不是驗(yàn)證模型元素,而是對實(shí)現(xiàn)本身進(jìn)行建模,以使任何具體的模型能夠重用這些元模型元素。為此,我們需要擴(kuò)展 Ecore 元模型。


擴(kuò)展了的元模型

本文附帶了一個高度簡化的用來擴(kuò)展 Ecore 的編程式模型。它不是一個完整或連貫的元模型或框架;嚴(yán)格來講,它是一個元素的原型集合,用于演示使用 EMF 對代碼實(shí)現(xiàn)進(jìn)行元建模的能力。圖 1 顯示了我們的擴(kuò)展元模型示例 EcoreX 的快照,下面是每個元素的簡短描述。


圖 1. EcoreX 模型
EcoreX 模型

EcoreX 元素

EPackageX 擴(kuò)展 EPackage
這是 Ecore 元素 EPackage 的一個簡單 “標(biāo)記” 擴(kuò)展,沒有任何附加屬性。這個元素是必需的,因?yàn)樵谀J(rèn)情況下,元素 EPackage EMF 編輯器插件不允許將 EClass 的子類作為子元素添加(參閱下面的 EClassX)。通過提供一個可擴(kuò)展 EPackage 的模型元素,代碼將會自動生成,從而允許將一個 EClassX 子元素添加到 EPackageX 中。
EClassX 擴(kuò)展 EClass
同樣地,這是 Ecore 元素 EClass 的一個簡單標(biāo)記擴(kuò)展,沒有任何附加屬性。與上面的元素類似,此元素也是必需的,因?yàn)樵谀J(rèn)情況下,EClass 的編輯器插件不允許添加 EOperation 的子類 — 這正是我們要在本文中實(shí)現(xiàn)的目標(biāo)。
EOperationImpl 擴(kuò)展 EOperation
這是用于向 Ecore 模型添加具體的元功能的基本實(shí)體和入口點(diǎn)。此元素被賦予 Ecore 的基礎(chǔ) EOperation 元素中沒有的屬性。下面描述的所有其他元素都屬于 EOperationImpl 并用于構(gòu)成編程式實(shí)現(xiàn)。例如,EOperationImpl 包含變量和語句,可以返回一個引用或值。
LocalVariable 擴(kuò)展 ETypedElement
LocalVariable 是一個本地變量。變量包含一個名稱和一個 Java 類型(比如 String、Integer、Object),而且由于這些屬性已經(jīng)存在于其超級超類(super-superclass)EParameter 中,所以 LocalVariable 不需要額外屬性。
Statement 擴(kuò)展 EClass
在我們的簡化邏輯模型中,一個 EOperationImpl 包含許多將會按給定順序計(jì)算的 statement。Statement 是一個抽象超類。
LiteralAssignment 擴(kuò)展 Statement
LiteralAssignment 引用一個變量,并且有一個 String 屬性,允許用戶輸入一個要被解析的值并將其分配給一個變量(例如,“hello”、“4.5” 可以分別分配給 Stringfloat)。
Access 擴(kuò)展 Statement
Access 表示引用 Java 字段或操作的動作。
FieldReferenceAssignment 擴(kuò)展 Access
訪問一個字段,以分配一個值(例如,var1 = var2.name)。
Invoke 擴(kuò)展 Access
調(diào)用一個操作(Java 方法)。Invoke 的結(jié)果可以分配給一個變量(例如,myVar = obj.toString())。

圖 2 展示了 EcoreX 元模型的一種更加類似 UML 的表示。


圖 2. Ecorex 模型圖
Ecorex 模型圖


入門

本文包括六個高級的步驟:

  1. 擴(kuò)展 Ecore 元模型,添加新語義
  2. 為被擴(kuò)展的元模型創(chuàng)建一個 genmodel
  3. 為此元模型生成一個 EMF 編輯器,并作為插件安裝。
  4. 使用這個新編輯器,構(gòu)建一個具體的模型來描述編程行為。
  5. 為這個具體的模型創(chuàng)建并配置一個 genmodel
  6. 基于這個具體的模型生成具體的 Java 代碼。

可以創(chuàng)建或?qū)肷厦婷枋龅脑P汀煞N情況都需要從一個現(xiàn)有 EMF 項(xiàng)目或創(chuàng)建一個新項(xiàng)目入手(New > Other > Eclipse Modeling Framework > Empty EMF Project)。我們的項(xiàng)目名為 EMFX,并且它應(yīng)包含一個名為 model 的文件夾。可以將這個 EcoreX.ecore 模型(參見 參考資料)復(fù)制到 model 目錄并跳至 構(gòu)建和啟動 Editor Metamodel 插件 小節(jié),也可以執(zhí)行以下步驟,從頭創(chuàng)建一個元模型。

擴(kuò)展 Ecore 元模型 — 從頭開始

右鍵單擊項(xiàng)目,從上下文菜單中選擇 New > Other > Example EMF Model Creation Wizards > Ecore Model。(對于 Eclipse V3.5+ [Galileo, Helios],則應(yīng)選擇 New > Other > Eclipse Modeling Framework > Ecore Model。)選擇 model 文件夾和名稱 EcoreX.ecore。

默認(rèn)情況下,我們將模型包稱為 ecorex。在模型窗口中右鍵單擊并選擇 Load Resource > Browse Registered Packages。選擇具有名稱空間 http://www./emf/2002/Ecore 的 Ecore Model。

導(dǎo)入 Ecore 元模型之后,就可以對其進(jìn)行擴(kuò)展了。要重新創(chuàng)建 ecorex.ecore 模型,首先在包元素 ecorex 上右鍵單擊并選擇 New Child EClass。將此元素稱為 EPackageX(參閱上面的模型元素描述)。然后需要將基元素 EPackage 作為這個新元素的 ESuper Type 添加。

通過將 EClass 指定為 ESuperType,使用相同的過程創(chuàng)建新元素 EClassX。根據(jù)需要對 Ecore 對象劃分子類,在 EcoreX 模型中繼續(xù)定義其他 EClass。使用圖 1 和 EcoreX.ecore 文件了解要為哪個 EClass 創(chuàng)建什么屬性。

構(gòu)建并啟動 the Editor Metamodel 插件

I在構(gòu)建步驟中,我們將創(chuàng)建元模型 genmodel 并構(gòu)建模型和編輯器項(xiàng)目。右鍵單擊 EcoreX 項(xiàng)目并選擇 New > Other > Eclipse Modeling Framework > EMF Model。(對于 EMF V2.5+ [Galileo, Helios],則應(yīng)選擇 New > Other > Eclipse Modeling Framework > EMF Generator Model。)可以提供一個名稱或接受默認(rèn)的名稱 EcoreX.genmodel。EcoreX 模型應(yīng)該被預(yù)選擇為 genmodel 的基模型。單擊 Load 驗(yàn)證 EcoreX.ecore 元模型。


圖 3. 新 EMF 模型
新 emf 模型

當(dāng)要求指定要生成和從其他生成器模型引用的包時(shí),選擇 Root packages 下面的 EcoreX 包和 Referenced generator models 下面的 Ecore。

現(xiàn)在,向?qū)樵P蛣?chuàng)建一個 genmodel。突出顯示 genmodel 中的頂級元素之后,從上下文菜單中選擇 Generate All,這樣可以自動生成關(guān)聯(lián)的代碼。根據(jù)在 genmodel 中配置的行為,這將生成 4 個 Eclipse 項(xiàng)目。本文不會關(guān)注 .test 項(xiàng)目,所以您可能不希望生成這個插件。

現(xiàn)在我們繼續(xù)啟動步驟。在大多數(shù) Eclipse 教程中,都會要求您在單獨(dú)的 Eclipse 過程中啟動所開發(fā)的插件。在本節(jié)中,我們將采用一種不同的方法:我們將在當(dāng)前 Eclipse 和工作區(qū)中激活插件。這樣更容易將預(yù)構(gòu)建的元模型與下一節(jié)中具體的模型開發(fā)集成。為此:

  1. 雙擊 EMFX plugin.xml 打開插件配置編輯器。
  2. 單擊 Exporting 選項(xiàng)卡下的 Export Wizard。
  3. 選擇基本的建模插件和兩個編輯器插件。
  4. Destination 選項(xiàng)卡下,選擇 Eclipse 安裝目錄,或托管存儲庫(如果可用)。

圖 4. 導(dǎo)出
導(dǎo)出的方法

注意:如果使用 –console 選項(xiàng)啟動 Eclipse,您可以使用 OSGi 命令控制臺動態(tài)地啟動、停止、更新和刷新插件(插件組),無需重新啟動 Eclipse 或啟動一個單獨(dú)的實(shí)例。

單擊 Finish 時(shí),會自動構(gòu)建生成的插件 JAR 文件,并自動將其復(fù)制到插件目錄。此時(shí),您需要重新啟動 Eclipse,激活新插件。現(xiàn)在我們已經(jīng)準(zhǔn)備好啟動編輯器插件了,創(chuàng)建一個新項(xiàng)目來保存我們的具體模型(我們的模型命名為 Test2)。

在這個新項(xiàng)目中,導(dǎo)航到 New > Other Example EMF Model Creation Wizards > Ecorex Model 并提供一個模型名稱。注意:在 EMF 的最新版本 (V2.5+) 中, 具體模型的文件擴(kuò)展名必須被設(shè)為 .ecore,而不是 .ecorex;否則,這個具體的 genmodel 將不能在后續(xù)步驟中被成功創(chuàng)建。選擇 EPackageX 元素。您現(xiàn)在有了一個空的具體模型。后續(xù)小節(jié)將討論如何構(gòu)建這些編程模型元素;完成后的文件 My.ecore 可以在 參考資料 部分找到。


建模具體的測試模型

在本節(jié)中,我們將對一個具體的 Java 類(EClassX 的實(shí)例)進(jìn)行建模,這個類包含兩個具體的方法,我們將對這兩個方法的實(shí)現(xiàn)進(jìn)行建模。第一個示例方法接受 String 參數(shù)消息,并輸出消息和一個時(shí)間戳 — 這有利于調(diào)試消息。以下是期望結(jié)果的表示。


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

第二個示例接受 3 個基于日期的參數(shù),并返回一個數(shù)字值,表示該日期對應(yīng)的是星期幾。


清單 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;
            }
            

第一步是填入在上一節(jié)最后一步中創(chuàng)建的新 EPackageX 元素下的 3 個必需屬性。如果在建模窗口下看不到 Properties 選項(xiàng)卡,可以從上下文菜單中選擇 Show Properties View。在這個示例中,我們的包名為 mypackage。


圖 5. EPackageX 屬性
EPackageX 屬性

接下來,向 mypackage 添加一個新 EClassX。可以在 mypackage 突出顯示時(shí)使用上下文菜單完成此任務(wù)。填入 name 屬性,為類提供一個名稱(比如 MyClass),向新類添加兩個 EOperationImpl 元素,并為它們指定方法名 printTimeStampMessagegetDayofWeek。然后,向每一個操作添加 Ecore 參數(shù)。


圖 6. EOperationImpl getDayOfWeek()
EOperationImpl getdayofweek()


圖 7. getDayOfWeek() 屬性
getDayOfWeek() 屬性

上面的操作 printTimestampMessage() 接受一個 EString 類型的參數(shù),而 getDayOfWeek() 接受 3 個 EInt 類型的參數(shù)。此外,操作 getDayOfWeek 返回一個 EInt,這可以在 property 屬性 EType 下進(jìn)行配置(參見圖 7)。

剖析 EOperationImpl

到現(xiàn)在為止,我們僅使用了繼承的 Ecore 元素和屬性?,F(xiàn)在是時(shí)候使用我們擴(kuò)展的元模型元素來構(gòu)建 Java 實(shí)現(xiàn)了。

LocalVariable
查看一下圖 8,printTimestampMessage() 將需要兩個 LocalVariable 元素 — 一個為 EString 類型,另一個為 ELong 類型。

圖 8. printTimestampMessage()
printTimestampMessage()


圖 9. LiteralAssignment
LiteralAssignment

在圖 9 中,Value 屬性的字符串被內(nèi)聯(lián)到 LiteralAssignment。您可以設(shè)想一個不同的元模型,其中的文字值(常量)被建模為單獨(dú)的元素。

接下來,我們插入一個 LiteralAssignment 類型的元素,它允許選擇一個 LocalVariable 并為其分配值。在本例中,我們選擇 String 變量并提供上面的原型方法中的文本值(記住在文本兩邊加上引號)。

DataType
再次查看上圖,注意,有一個名為 SystemType 的 Ecore DataType,它是 java.lang.System 的一個包裝器。必須將其添加到我們的 mypackage 包,因?yàn)樗鼘浑S后的 Invoke 元素引用。
Statement
添加到這個操作的第一個 StatementSystemType 中的靜態(tài)方法 currentTimeMillis() 的一個 Invoke,已經(jīng)在上面定義了。

圖 10. 調(diào)用 currentTimeMillis() 屬性
調(diào)用 currenttimemillis() 屬性

根據(jù)我們的元模型(我們將在下一節(jié)提供代碼模板),上面的 Invoke 將轉(zhuǎn)換為 Java 語句:timestamp = java.lang.System.currentTimeMillis();。

下一個 Invoke 與之前的那個稍有不同。首先,沒有 Assignment。其次,我們將把 message 參數(shù)的引用作為 Args 屬性的一個參數(shù)。


圖 11. 調(diào)用 out.print 屬性
調(diào)用 out.print 屬性

另外請注意,Access Name 屬性的值為 out.print — 在這里我們實(shí)際上從 Java System 間接引用了字段 out,然后調(diào)用方法 print。我們使用了這種快捷方式,而沒有結(jié)合使用一個 FieldReferenceAssignment 和一個 PrintStream 類型的 LocalVariable

操作中的第 3 個(最后一個)Invoke 是一個使用 LocalVariable timestamp 作為單個參數(shù)的 println()。這就完成了具體操作 printTimestampMessage() 的建模。

讓我們看看第二個 EOperationImpl getDayOfWeek() 的完整模型。


圖 12. getDayOfWeek()
getDayOfWeek()

DataTypes
在模型的底部,我們創(chuàng)建了一個額外的 DataType,名為 CalendarType,這是該操作所必需的。
LocalVariables
在操作模型的 3 個 LocalVariable 中,我們主要關(guān)注稱為 resultLocalVariable,因?yàn)樗鼘4鎴?zhí)行完操作的最后一條語句之后返回的值。在 EOperationImpl 屬性中有一個名為 Return Ref 的屬性,而且在我們的實(shí)現(xiàn)中,我們使用下拉菜單選擇 LocalVariable 結(jié)果。
Statement
正如圖 12 所示,3 個 LocalVariable 之后是 3 個 Statement。第一個是 Invoke,它使用 CalendarType 元素上的 getInstance(),為 calendar 變量分配一個值,與圖 10 中的操作類似。

接下來是對 calendar 變量執(zhí)行的 set() 方法的 Invoke,現(xiàn)在它傳遞 3 個與 EOperationImpl 參數(shù)(year、monthdate)相對應(yīng)的 Arg。


圖 13. 帶有參數(shù)的 set()
帶有參數(shù)的 set()


圖 14. FieldReferenceAssignment
FieldReferenceAssignment

根據(jù)我們的元模型,這個元素將會生成與 DAY = Calendar.DAY_OF_WEEK; 類似的 Java 代碼。

在圖 15 中,DAY 變量用于這個 EOperationImpl 的最后一個 Invoke:一個 get(),其返回值被分配給變量 result(我們的實(shí)現(xiàn)的 Return Ref


圖 15. Return Ref
Return ref 

實(shí)現(xiàn)動態(tài)模板

我們現(xiàn)在設(shè)計(jì)了一個擴(kuò)展的元模型,并用其描述了一個具體的模型 My.ecore(請參見上述的 EMF V2.5+ 文件名稱說明)。現(xiàn)在終于可以用 JET 最終實(shí)現(xiàn)一些代碼實(shí)現(xiàn)了。要查看 JET 模板的語法突出顯示功能,您需要安裝 JET Editor Plugin(參見 參考資料 和 “JET Editor 局限性”)。

默認(rèn)情況下,在為模型生成代碼時(shí),EMF 不會使用動態(tài)模板。它使用預(yù)構(gòu)建的 Java 類。要開始定制 JET 模板,我們需要從插件 JAR 文件 org.eclipse.emf.codegen.ecore_2.3.0.XYZ.jar 復(fù)制一些文件,其中 XYZ 是 Eclipse 插件文件夾中您的 EMF 版本的時(shí)間戳。本文使用 org.eclipse.emf.codegen.ecore_2.3.0.v200706262000.jar。要復(fù)制這些文件,請使用任意一種解壓縮工具打開 JAR 文件,并執(zhí)行以下操作:

  1. 從這個 JAR 文件將模板目錄提取到您的具體模型的 Java 項(xiàng)目中。
  2. 在模板/模型中創(chuàng)建一個目錄,名為 Class。
  3. Class 文件夾中創(chuàng)建一個新的空文件,名為 implementedGenOperation.TODO.override.javajetinc 或從 參考資料 中復(fù)制。

由名稱可以看出,第 3 步中的新文件是一個 JET 模板,我們將在其中加入模型對象 EOperationImpl 的代碼生成邏輯。默認(rèn)情況下,這個文件并不存在,因?yàn)?EMF 只為每個 EOperation 提供一個空的方法簽名。一旦激活了動態(tài)模板功能,我們的新文件將被作為 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 的詳細(xì)討論超出了本文的范圍。但是,因?yàn)?JET 模板對我們的操作過程至關(guān)重要,我們將在偽代碼方面回顧一下模板的內(nèi)容。請記住,在處理模板之前,第一個變量 genOperation 已經(jīng)被 Ecore/JET 預(yù)初始化。


清單 4. genOperation 被 Ecore/JET 預(yù)初始化
            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;
            

在構(gòu)建具體模型之前,需要執(zhí)行一些操作。首先,在 templates/model/Class.javajet 頂部,我們必須將以下內(nèi)容添加到導(dǎo)入列表(標(biāo)記為粗體的前兩行):

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

當(dāng)然,EcoreX 包是經(jīng)過擴(kuò)展的元模型。接下來,我們需要為我們的具體模型(My.ecore,類型為 '.ecorex')創(chuàng)建和配置一個 EMF(GenModel)。為此,在模型上右鍵單擊并選擇 New > Other > Eclipse Modeling Framework > EMF Model(對于 EMF V2.5+ [Galileo, Helios],應(yīng)選擇 New > Other > Eclipse Modeling Framework > EMF Generator Model。)創(chuàng)建完成之后,需要在屬性組 Templates & Merge 下配置 3 個屬性,在 Model 下配置第四個屬性。


圖 16. GenModel — Templates & Merge
GenModel

  1. Dynamic Templates 設(shè)置為 true。
  2. 指定 Template Directory。
  3. 將 EMFX(擴(kuò)展的元模型插件 ID)添加到 Template Plug-in Variables
  4. 最近版本:在 Model 組屬性下,將 Suppress Interfaces 設(shè)置為 true。

現(xiàn)在可以進(jìn)行構(gòu)建了,右鍵單擊 GenModel 并選擇 Generate Model Code。 如果一切順利,在具體的 Test 項(xiàng)目(我們的項(xiàng)目稱為 Test2)的源文件夾(src)中,您應(yīng)該可以看到生成的 Java 源代碼包和類,其中一個名為 mypackage.impl.MyClassImpl.java。打開該文件,您應(yīng)該會看到兩個生成的方法。


清單 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 之前,正如上面的幾個屏幕快照所示,從一個擴(kuò)展了的 Ecore 模型生成的具體模型應(yīng)該保留 '.ecorex' 的擴(kuò)展名(如創(chuàng)建時(shí)的向?qū)ㄗh的那樣)。這有助于區(qū)別擴(kuò)展了的模型與 ‘初級的’ Ecore 模型。然而,在 EMF 的最新版本中,genmodel 向?qū)Вㄈ缭趫D 16 之前所解釋的)不接受除 .ecore 之外的其他文件擴(kuò)展名。

JET Editor 局限性

要獲得 JET 模板的語法突出顯示功能,您需要安裝 Eclipse JET Editor(JET Editor Plugin 最近已經(jīng)從 EMF 遷移到 M2T)。

但是,在撰寫本文時(shí),JET Editor 的最新版本不能正確處理 Java 內(nèi)容幫助或嵌套 JET 包含文件(比如 .javajetinc 文件)的動態(tài)編譯。此外,為了確保構(gòu)建成功,只能在父文件(比如上面的 Class.javajet)中指定導(dǎo)入操作,而不能在包含的文件中指定。

實(shí)際上,使用一些額外配置(即,使用項(xiàng)目的上下文菜單),您可以將 EMF 動態(tài)模板項(xiàng)目(本文示例中的 Test2)轉(zhuǎn)換為 JET 項(xiàng)目。在實(shí)踐中,上面提及的局限性以及 EMF 和 M2T/JET 之間缺少集成,使得這種方法不太可行。

因此,很難捕獲和改正包含的模板文件中的錯誤。由于在生成最終代碼之前,JET 模板首先會被編譯為一種中間 Java 文件(默認(rèn)情況下位于一個隱藏的 Java 項(xiàng)目 JETEmitter 中),所以通過從 Eclipse 的 Package Explorer 視圖中刪除過濾器,您能夠看到這些編譯錯誤。如果這只是模板文件中的格式錯誤,在構(gòu)建期間將會出現(xiàn)一個 Eclipse 彈出窗口?;蛟S在未來的 JET 版本中,我們會看到更多的改進(jìn)功能。

未進(jìn)行模型驗(yàn)證

本文中的示例未使用 EMF Validation Framework 或 OCL 功能。所以,模型中的不一致性將會導(dǎo)致構(gòu)建失敗,例如,一個 EOperationImpl 可以聲明某種返回類型,但是 Return Ref 屬性可以引用不同的類型或者為空。在構(gòu)建模型期間,將不會發(fā)現(xiàn)這些錯誤,而且生成的代碼將無法編譯??梢詫υP瓦M(jìn)行改進(jìn),使用 OCL 增強(qiáng)完整性和約束(參見 參考資料)。

結(jié)束語

我們看到了如何擴(kuò)展 Ecore 元模型,將合成的 Java 方法中的編程式行為概念化。通過導(dǎo)入 Ecore 本身,我們擴(kuò)展了一些 Ecore 模型元素 — 尤其是 EOperation。然后構(gòu)建了元模型,并使用編輯器設(shè)計(jì)了一個具體的測試模型,包括以 EOperationImpl 形式建模的兩個 Java 方法。我們配置并構(gòu)建了 JET 模板,用于為 EOperationImpl 生成代碼。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多