級(jí)別: 初級(jí)
Adrian Powell, Advisory I/T Specialist, IBM
2004 年 4 月 01 日
Eclipse Modeling Framework(EMF)是一個(gè)開放源代碼的模型驅(qū)動(dòng)應(yīng)用程序開發(fā)框架。它可以基于 XML Schema、UML 或經(jīng)過注釋的 Java 中指定的模型,創(chuàng)建 Java 代碼,實(shí)現(xiàn)圖形化的數(shù)據(jù)編輯、操縱、讀取和序列化。EMF 是 IBM WebSphere Studio 和 Eclipse 項(xiàng)目中很多工具的基礎(chǔ)。本文將幫助您逐步了解創(chuàng)建模型、生成代碼、使用生成的應(yīng)用程序和定制編輯器的整個(gè)過程。
EMF 究竟是什么?
Eclipse Modeling Framework(EMF)是一個(gè)開放源代碼的框架,它的目標(biāo)是實(shí)現(xiàn)模型驅(qū)動(dòng)架構(gòu)(Model-Driven Architecture)的開發(fā)。如果我們當(dāng)中的少數(shù)人有幸得到了某個(gè) UML 模型,那么這個(gè)框架就可以幫助我們將文檔變成代碼。至于其他人,這個(gè)工具也使您又有一次機(jī)會(huì)向老板證實(shí),把時(shí)間花在為解決方案建模上是值得的。除了可以生成令人贊嘆的 Java 代碼之外,EMF 還可以生成 Eclipse 插件,以及圖形化的可定制編輯器。當(dāng)您改變模型時(shí)(這種情況真的會(huì)出現(xiàn)),EMF 可以通過單擊一個(gè)按鈕,就使代碼和模型保持同步。
EMF 生成的代碼也不是一種只配丟進(jìn)垃圾箱的解決方案。這種代碼支持標(biāo)準(zhǔn)的創(chuàng)建、獲取、更新和刪除操作,而且還支持元數(shù)約束、復(fù)雜關(guān)系和繼承結(jié)構(gòu)、屏蔽定義,以及一套屬性描述。生成的代碼還提供通知、參照完整性和可定制的 XMI 持久性。您所需要做的全部工作就是創(chuàng)建一個(gè)對(duì)象模型,就像您以前也想做的那樣。
EMF 是比較新的事物,但前景廣闊,對(duì)它持續(xù)支持的力度也很強(qiáng)。它實(shí)現(xiàn)的是一項(xiàng)公共標(biāo)準(zhǔn),即對(duì)象管理組織(Object Management Group)的元對(duì)象工具(Meta-Object Facility,MOF)。現(xiàn)在 EMF 已經(jīng)對(duì) MOF 的第二版進(jìn)行了增強(qiáng)。更進(jìn)一步看,EMF 還是 EMF:XSD 以及 Hyades 等 Eclipse 項(xiàng)目的基礎(chǔ),大多數(shù) IBM WebSphere Studio 產(chǎn)品也都使用它。EMF 第二版的開發(fā)已經(jīng)開始,開發(fā)構(gòu)建應(yīng)該很快就會(huì)出爐。第二版開發(fā)計(jì)劃中包括更好的 XML Schema 支持、更靈活的代碼生成方式以及模型之間的映射機(jī)制。
讓工具自己說話
商業(yè)宣傳已經(jīng)說得夠多了。現(xiàn)在讓我們直接進(jìn)入代碼中,看看 EMF 到底能做些什么。下面的例子都是用 Eclipse 3.0M7 和 EMF 2.0.0,再加上與之匹配的 XSD 工具箱實(shí)現(xiàn)的。現(xiàn)在有四種獨(dú)立的 EMF 開發(fā)流程,每一種都適用于不同版本的 Eclipse,所以一定要保證根據(jù)您的 Eclipse 版本選擇了正確的 EMF 版本(請(qǐng)參閱 參考資料中的鏈接,獲取這些插件)。
我們將以一個(gè)簡單的 Web 論壇為例,向您展示最重要的特性。模型的根為 Forum ,下面包括一組 Member 和 Topic 。每一個(gè) Topic 都具有一個(gè) TopicCategory (枚舉類型), Member 和 Topic 通過 Post 類間接相關(guān)聯(lián),這兩者之間也存在直接關(guān)聯(lián),因?yàn)?Member 可以創(chuàng)建 Topic 。
用 UML 和 Omondo 創(chuàng)建 EMF 模型
Omondo 的 UML 插件是在 Eclipse 中創(chuàng)建 UML 文檔的方便可靠的工具。它看起來就像是 Rational Rose 受冷落的小兄弟,但除非是您需要特別強(qiáng)大的功能,否則用它就可以工作得很好了。不過,該工具尚不支持 Eclipse 3,所以我采用 Eclipse 2.1 來創(chuàng)建 UML 類圖。
一開始,我們創(chuàng)建一個(gè)新的 Java 項(xiàng)目 UMLForum,以及一個(gè)新包 com.ibm.example.forum 。再創(chuàng)建一個(gè)新的 EMF 類圖, forum.ucd ,存放在 src/com/ibm/example/forum 下。目錄中創(chuàng)建了兩個(gè)文件,forum.ecd 和 forum.ecore。向類圖中增加一個(gè)新類,名為 Forum ,然后單擊 Finished。向 Forum 類中增加一條屬性描述,類型為 EString (對(duì)于所有的簡單 Java 類都有相應(yīng)的 Ecore 類),如圖 1 所示。對(duì)于屬性的特性,只選擇 changeable ,并將范圍設(shè)為從 0 到 1。
如果您過一會(huì)改主意了,想使用其他的特性,可以打開 Properties 視圖,選擇其中的類或?qū)傩浴?/p>
圖 1. 新建的 Forum 類及其屬性的性質(zhì)
對(duì)于下列接口重復(fù)上述步驟:
接口 |
屬性 |
類型 |
Member |
nickname |
EString |
Topic |
title |
EString |
Post |
comment |
EString |
為定義關(guān)聯(lián),我們可以選中關(guān)聯(lián)按鈕,然后單擊關(guān)聯(lián)的源( Forum )和目標(biāo)( Member )。這樣將打開關(guān)聯(lián)屬性設(shè)置對(duì)話框。在其中將名字設(shè)置為 members ,確保僅僅選擇了 changeable 和 containment,然后將上限設(shè)為 -1。在第二個(gè) Association End 選項(xiàng)卡中,取消選中的 Navigable,然后單擊 Ok。對(duì) Forum 和 Topic 也執(zhí)行相同的操作,屬性名稱從 members 改為 topics 。取消選中的 navigable,從而創(chuàng)建一個(gè)無方向的關(guān)聯(lián),但我們想讓其他屬性都保持為雙向。
按照下表所示完成關(guān)聯(lián)設(shè)置:
源 |
目標(biāo) |
關(guān)聯(lián) |
名稱 |
特性 |
范圍 |
Member |
Topic |
1st Association |
topicsCreated |
changeable |
0 到 1 |
|
|
2nd Association |
creator |
changeable |
0 到 1 |
Topic |
Post |
1st Association |
posts |
Containment, changeable |
0 到 -1 |
|
|
2nd Association |
topic |
changeable |
0 到 1 |
Member |
Post |
1st Association |
posts |
changeable |
0 到 -1 |
|
2nd Association |
author |
changeable |
0 到 1 |
最后,我們要定義一個(gè)枚舉類型,用于表示 topic 有多少不同的類型。創(chuàng)建一個(gè)新的枚舉類型,名字叫做 TopicCategory 。Literal 中加入以下的內(nèi)容:
ANNOUNCEMENT , value = 0
GUEST_BOOK , value = 1
DISCUSSION , value = 2
然后,為 Topic 定義一個(gè)新屬性,叫做 category ,類型為 TopicCategory ,changeable,范圍 0-1。如果您愿意的話,可以在屬性標(biāo)簽上對(duì)默認(rèn)值進(jìn)行修改,但我們將接受 ANNOUNCEMENT 的默認(rèn)值。
圖 2. 完成后的 UML 類模型
一旦您完成了圖 2 所示的 UML 類圖,下一步就是創(chuàng)建一個(gè) EMF 模型。為此,需要先創(chuàng)建一個(gè)新的 EMF 項(xiàng)目( File > New > Project... > Eclipse Modeling Framework > EMF Project),并用 com.ibm.example.forum 作為該項(xiàng)目的名稱(這是插件名稱的基礎(chǔ),因此我們遵從 Eclipse 插件的命名規(guī)范)。在下一個(gè)頁面上,選擇 Load from an EMF core model,然后單擊 Next。從文件系統(tǒng)中加載 ecore 文件,它將自動(dòng)填充 Generator 的模型名。在最后一個(gè)頁面上,單擊包旁邊的復(fù)選框,然后單擊 Finish。這樣就創(chuàng)建好了 EMF 模型,它的名字叫做 forum.genmodel。您可以從 使用生成的 EMF 模型一節(jié)中了解到這個(gè)模型是什么,以及如何使用它。
用 XML Schema
創(chuàng)建 EMF 模型
XML Schema(XSD)的表現(xiàn)能力不如 UML 或帶注釋的 Java 代碼那么強(qiáng)大,例如,它不能表達(dá)出雙向引用的關(guān)聯(lián)。但是由于默認(rèn)的的序列化方法要使用到您的方案,因此 XSD 對(duì)定制序列化來說是最快的方法。如果您希望為模型生成非常詳細(xì)的 XML/XMI,那么 XSD 就是必然的選擇。
清單 1. forum.xsd 的片段
<xsd:simpleType name="TopicCategory">
<xsd:restriction base="xsd:NCName">
<xsd:enumeration value="Announcement"/>
<xsd:enumeration value="GuestBook"/>
<xsd:enumeration value="Discussion"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="Post">
<xsd:sequence>
<xsd:element name="comment" type="xsd:string"/>
<xsd:element name="author" type="xsd:anyURI" ecore:reference="forum:Member"/>
<xsd:element name="topic" type="xsd:anyURI" ecore:reference="forum:Topic"/>
</xsd:sequence>
</xsd:complexType>
|
在清單 1 中,您可以看到枚舉是如何表示的,也能從中了解到如何定義一個(gè)具有指向其他類型的元素和引用的類型。在 Forum 這個(gè)例子中,我們僅僅使用了字符串屬性 "xsd:string" ,但是其他簡單 Java 類型也是支持的。有關(guān) XML Schema 和 forum.xsd 文件的更多信息,請(qǐng)參閱 參考資料。
一旦完成了 XSD,下一步就是創(chuàng)建 EMF 模型。方法與 UML 模型中類似,先創(chuàng)建一個(gè)新的 EMF 項(xiàng)目( File > New > Project... > Eclipse Modeling Framework > EMF Project),項(xiàng)目名稱為 com.ibm.example.forum(這是插件名稱的基礎(chǔ),因此我們遵從 Eclipse 插件的命名規(guī)范)。在下一個(gè)頁面上選擇 Load from an XML Schema,然后單擊 Next。在文件系統(tǒng)中找出 XSD 文件并加載,然后 Generator 中的模型名就會(huì)自動(dòng)填充。在最后一個(gè)頁面上,單擊包旁邊的復(fù)選框,然后單擊 Finish。這樣就創(chuàng)建了一個(gè) EMF 模型,名字叫做 forum.genmodel。 您可以從 使用生成的 EMF 模型一節(jié)中了解到這個(gè)模型是什么,以及如何使用它。
用帶注釋的 Java 代碼創(chuàng)建 EMF 模型
如果通過 Java 代碼定義 EMF 模型,我們可以用 Interface 列出每一個(gè)類的屬性,以及類之間的關(guān)系。這樣得到的內(nèi)容并不充足,無法定義我們想要的全部信息,所以 EMF 使用了特殊的 JavaDoc 標(biāo)簽。每一個(gè)屬性或類,如果是 EMF 模型的一部分,就必須在其 JavaDoc 中包含一個(gè) @model 標(biāo)簽,也可以包含一個(gè)附加屬性列表。比如說,如果要構(gòu)造如上面圖 2 所示的一個(gè)對(duì)象模型,我們對(duì) Forum 的定義看起來應(yīng)該像清單 2 的樣子。
清單 2. 帶注釋的 Forum.java
package com.ibm.example.forum;
import java.util.List;
/**
*
@model
*/
public interface Forum {
/**
*
@model type="Topic" containment="true"
*/
List getTopics();
/**
*
@model type="Member" containment="true"
*/
List getMembers();
/**
*
@model
*/
String getDescription();
}
|
清單 2 聲明了一個(gè)叫做 Forum 的對(duì)象,它具有一條 String 類型的描述信息和兩個(gè)孩子,一個(gè)是 Topic 列表,還有一個(gè)是 Member 列表。這兩個(gè)孩子都包含在 Forum 之內(nèi)。
對(duì)于簡單的屬性,如 描述信息 , @model 標(biāo)簽就足夠了,但對(duì)于 list 而言,您也需要為其指明類型。 containment 屬性是可選的,但是如果某個(gè)對(duì)象是被包含的,那么它就和其容器一起被序列化。為了簡化序列化的過程,我們要保證所有的對(duì)象都是直接或者間接包含在 Forum 中的。其他一些有用的可選屬性如下:
opposite (用于雙向?qū)傩裕?
default (屬性的默認(rèn)值)。
transient (該屬性不能被序列化)。
要獲得完整的屬性列表,請(qǐng)您參閱 參考資料中的 EMF user's guide。
惟一需要當(dāng)心的是枚舉類型。它被定義成一個(gè) Class,而不是其他模型類中的 Interface! 為了明確這一點(diǎn),清單 3 展示了 TopicCategory 枚舉類型是如何實(shí)現(xiàn)的。
清單 3. 枚舉類型 TopicCategory.java
package com.ibm.example.forum;
/**
* @model
*/
public
class TopicCategory{
/**
* @model name="Announcement"
*/
public static final int ANNOUNCEMENT = 0;
/**
* @model name="GuestBook"
*/
public static final int GUEST_BOOK = 1;
/**
* @model name="Discussion"
*/
public static final int DISCUSSION = 2;
}
|
最后,生成如下所示的三個(gè)接口,模型就完成了:
接口 |
方法 |
模型標(biāo)簽 |
Member |
List getPosts() |
type="Post" opposite="author" |
|
List getTopicsCreated() |
type="Topic" opposite="creator" |
|
String getName() |
|
Topic |
List getPosts() |
type="Post" opposite="author" |
|
Member getCreator() |
opposite="topicsCreated" |
|
String getTitle() |
|
|
TopicCategory getCategory() |
|
Post |
Member getAuthor |
opposite="posts" |
|
Topic getTopic() |
opposite="posts" |
|
String getComment() |
|
模型定義完成之時(shí),可以生成 EMF 模型( File > New > Other > Eclipse Modeling Framework > EMF Models)。將父目錄設(shè)為 com.ibm.example.forum/src/model, File name設(shè)為 forum.genmodel。在下一個(gè)頁面上,選擇 Load from annotated Java,然后選中包“forum”旁邊的復(fù)選框。然后單擊 Finish。這樣就創(chuàng)建了一個(gè)名為 forum.genmodel 的 EMF 模型。
使用生成的 EMF 模型
現(xiàn)在您的工作空間中應(yīng)該有一個(gè)生成好的 EMF 模型 forum.genmodel。這個(gè)模型中包含您輸入其中的所有信息。用默認(rèn)的編輯器打開這個(gè)模型(參見圖 3),再打開 Properties 視圖,然后檢查模型樹中每一個(gè)節(jié)點(diǎn)的屬性。前面輸入的所有屬性都可以定制,但是也有一些用于定制代碼生成的屬性。為了驗(yàn)證這一點(diǎn),讓我們?cè)囍薷?#8220;Copyright Text”或“Generate Schema”之類的屬性,看看會(huì)發(fā)生什么事情。
圖 3. 在默認(rèn)的編輯器中打開生成的 EMF 模型
如果對(duì)模型描述(UML、XSD、帶注釋的 Java)進(jìn)行了修改,也可以在 Package Explorer 中用右鍵單擊該模型,然后選擇 Reload,這樣就能夠重新加載模型。這實(shí)現(xiàn)了用 EMF 生成的模型與模型描述之間的同步。重新加載后將會(huì)改變您在生成的模型中修改過的屬性。
生成 Java 代碼
如果您對(duì)模型描述感到滿意,或者如果您僅僅是想看看所有這一切到底是什么意思,那么現(xiàn)在就可以生成代碼了。在根節(jié)點(diǎn)上單擊鼠標(biāo)右鍵,選擇其中一個(gè)生成選項(xiàng):Model、Edit、或 Editor code。 Generate Model將在當(dāng)前項(xiàng)目中創(chuàng)建該 EMF 模型的 Java 實(shí)現(xiàn)代碼。其中會(huì)包含下列內(nèi)容:
com.ibm.example.forum -- 創(chuàng)建該 Java 類的接口和工廠。
com.ibm.example.forum.impl -- com.ibm.example.forum 中定義的接口的具體實(shí)現(xiàn)。
com.ibm.example.forum.util -- AdapterFactory。
Generate Editor Code將創(chuàng)建 com.ibm.example.forum.edit 項(xiàng)目。其中僅僅包含一個(gè)包, com.ibm.example.forum.provider ,用于控制每一個(gè)模型對(duì)象出現(xiàn)在編輯器中的方式。 Generate Editor Code將在 com.ibm.example.forum.editor 項(xiàng)目中創(chuàng)建一個(gè)插件編輯器示例,其中包含了 com.ibm.example.forum.presentation。這些類提供了一系列簡單的 JFace 編輯器,可以與您的模型進(jìn)行交互。
為了測試生成的插件,請(qǐng)依次進(jìn)入 Run > Run... > Run Time Workbench > New。輸入一個(gè)描述性的名稱,然后在 plug-ins 選項(xiàng)卡中,選擇 launch with all workspace and enabled external plug-ins。再在 Common 頁下,單擊 Display in favorites menu > Run和 Launch in background。最后保存設(shè)置并運(yùn)行。
這時(shí)將出現(xiàn)一個(gè)新的 Eclipse 工作臺(tái),您可以在 Help > About Eclipse Platform > Plug-in Details下面驗(yàn)證您的插件是否可用,如圖 4 所示。
圖 4. Forum 的插件詳細(xì)信息
為了測試生成的插件,您可以創(chuàng)建一個(gè)新的 Simple 項(xiàng)目,名為“Forum Demo”,然后依次進(jìn)入 New > Other... > Example EMF Model Creation Wizards > Forum Model。給文件取名叫做 sample.forum,然后選擇 Forum 作為 Model Object。這時(shí)會(huì)打開一個(gè)窗口,您可以在這里向根中增加新的模型元素。其中包含幾種視圖:Selection、Parent、List、Tree、Table 和 TreeTable。所有這些視圖都顯示相同的數(shù)據(jù),也和 Outline 視圖保持同步。雖然所有視圖都會(huì)在右鍵菜單選項(xiàng)中顯示 New Sibling/New Child,但是我發(fā)現(xiàn),有些視圖在加入兄弟節(jié)點(diǎn)或子節(jié)點(diǎn)時(shí)不能正確響應(yīng)。如果您也遇到這種情況,可以使用 TableTree 視圖,或是在 Outline 視圖中創(chuàng)建新的節(jié)點(diǎn)。圖 5 展示了所生成的插件編輯器。
圖 5. 所生成的插件編輯器
定制生成的代碼
生成的代碼都很不錯(cuò),但是這只是真正應(yīng)用程序的起點(diǎn)。為了滿足我們的需要,我們必須對(duì)其進(jìn)行調(diào)整和定制。我們可以改變所生成的模型類的實(shí)現(xiàn),也可以對(duì)編輯器進(jìn)行擴(kuò)展和定制。好在 EMF 沒有讓我們失望,我們可以按照自己的想法做任何定制,當(dāng)重新生成代碼時(shí)也不會(huì)丟掉這些內(nèi)容。我們需要做的全部工作就是刪除 @generated JavaDoc 標(biāo)簽,EMF 的 jmerge 將保證這些方法、屬性或類不被打擾。
為著重說明您能對(duì)代碼進(jìn)行哪些修改,讓我們來看一個(gè)簡單的例子。在所生成編輯器的 Table 視圖中,兩個(gè)字段都顯示出相同的的值。這一點(diǎn)并不是完全沒有用處。為了改善一下,我們可以修改第二個(gè)字段,讓它在選中一個(gè) Topic 的時(shí)候顯示 Author,然后增加第三個(gè)字段,給出該 Topic 中的帖子數(shù)。
第一步,向 Table 視圖中額外增加一個(gè)字段。這一步在 com.ibm.example.forum.editor 項(xiàng)目中實(shí)現(xiàn),即 createPages() 方法中的 com.ibm.example.forum.presentation.ForumEditor 。把 @generated 標(biāo)簽刪除,這樣就能持久保存我們的修改,然后定位到表瀏覽窗口所在的位置。按照清單 4 的內(nèi)容對(duì)這段代碼進(jìn)行修改。
清單 4. 修改后的 createPages()
TableColumn selfColumn = new TableColumn(table, SWT.NONE);
layout.addColumnData(new ColumnWeightData(2, 100, true));
selfColumn.setText("Author");
selfColumn.setResizable(true);
TableColumn numberColumn = new TableColumn(table, SWT.NONE);
layout.addColumnData(new ColumnWeightData(4, 100, true));
numberColumn.setText("Number of Posts");
numberColumn.setResizable(true);
tableViewer.setColumnProperties(new String [] {"a", "b", "c"});
|
這樣就額外增加了一個(gè)字段,但是現(xiàn)在所有的三個(gè)字段都顯示相同的數(shù)據(jù)。為了定制每一個(gè)字段中的數(shù)據(jù),我們需要提供一些 ITableItemLabelProvider 的實(shí)現(xiàn)。打開 com.ibm.example.forum.provider.TopicItemProvider ,在實(shí)現(xiàn)列表中加入 ITableItemLabelProvider 。我們需要增加兩個(gè)方法, getColumnText(Object, int) 和 getColumnImage(Object, int) ,如清單 5 所示。
清單 5. 加入 TopicItemProvider
public String getColumnText(Object obj, int index) {
if( index == 0 ){
return getText(obj);
}
else if( index == 1 ) {
return ((Topic)obj).getCreator().getNickname();
} else if( index == 2 ) {
return " + ((Topic)obj).getPosts().size();
}
return "unknown";
}
public Object getColumnImage(Object obj, int index) {
return getImage( obj );
}
|
最后,我們需要注冊(cè)這個(gè)提供程序。實(shí)現(xiàn)方法是編輯 com.ibm.example.forum.provider.ForumItemProviderAdapterFactory 的構(gòu)造函數(shù),向支持的類型中增加 ITableItemLabelProvider ,如清單 6 所示。
清單 6. ForumItemProviderFactory 構(gòu)造函數(shù)
public ForumItemProviderAdapterFactory() {
supportedTypes.add(ITableItemLabelProvider.class);
supportedTypes.add(IStructuredItemContentProvider.class);
supportedTypes.add(ITreeItemContentProvider.class);
supportedTypes.add(IItemPropertySource.class);
supportedTypes.add(IEditingDomainItemProvider.class);
supportedTypes.add(IItemLabelProvider.class);
}
|
現(xiàn)在我們?cè)龠\(yùn)行這個(gè)插件,打開表視圖,就能看到圖 6。請(qǐng)注意,沒有實(shí)現(xiàn)的 ITableItemLabelProvider 元素將在所有的字段中顯示相同的文本。
圖 6. 修改后的 Table 編輯器
在 Java 中操縱模型
生成的模型代碼看起來就像是 Java 代碼中增加了一些有用的東西。系統(tǒng)還提供了一種靈活的定制反射 API,對(duì)工具很有用。您也許注意到了,這就是 eGet() 和 eSet() 兩個(gè)方法。在大多數(shù)情況下,我們并不需要關(guān)心它,所以我們還是看看我們感興趣的東西:如何創(chuàng)建、保存和加載模型。讓我們從頭開始:加載 EMF 模型。
清單 7. 加載 Forum
// Register the XMI resource factory for the .forummodel extension
Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
Map m = reg.getExtensionToFactoryMap();
m.put("forummodel", new XMIResourceFactoryImpl());
ResourceSet resSet=new ResourceSetImpl();
Resource res = resSet.getResource(URI.createURI("model/forum.forummodel"),true);
Forum forum = (Forum)res.getContents().get(0);
|
清單 7 展示了如何給文件關(guān)聯(lián)一個(gè)符合 XMI 格式的擴(kuò)展名“forummodel”,然后用 EMF 的 ResourceSet 解析并加載 forum 模型。我們知道,F(xiàn)orum 是惟一的根元素,所以可以想象, res.getContents().get(0) 將返回一個(gè)且僅有一個(gè) Forum 對(duì)象。如果情況不是這樣,我們還可以從 getContents().iterator() 中取出一個(gè) Iterator,然后分別檢查每一個(gè)元素。
我們還可以換一種方法,創(chuàng)建一個(gè)新的 Forum,然后用程序組裝起來,如清單 8 所示。
清單 8. 初始化 Forum
// initialize model and dependencies
ForumPackageImpl.init();
// retrieve the default Forum factory singleton
ForumFactory factory = ForumFactory.eINSTANCE;
Forum forum = factory.createForum();
forum.setDescription("programmatic forum example");
Member adminMember = factory.createMember();
adminMember.setNickname("Administrator");
forum.getMembers().add( adminMember );
Topic noticeTopic = factory.createTopic();
noticeTopic.setTitle("Notices");
noticeTopic.setCategory(TopicCategory.ANNOUNCEMENT_LITERAL);
noticeTopic.setCreator(adminMember);
forum.getTopic().add( noticeTopic );
|
在這個(gè)例子中,我們首先初始化包,然后創(chuàng)建 ForumFactory,用它生成所有的子對(duì)象。創(chuàng)建完畢之后,就可以像標(biāo)準(zhǔn)的 JavaBean 那樣訪問這些對(duì)象。然而,由于我們把 Topic 和 Memeber 之間的 creator/topicsCreated 關(guān)系聲明為雙向,當(dāng)我們調(diào)用 noticeTopic.setCreator(adminMember) 的時(shí)候, adminMember 的 topicsCreated 清單中就包括 noticeTopic 。
一旦我們創(chuàng)建并操縱了 EMF 模型,就很容易將其保存為我們選定的格式(參見清單 9)。
清單 9. 保存 Forum
URI fileURI = URI.createFileURI("model/forum.ecore");
Resource resource = new XMIResourceFactoryImpl().createResource(fileURI);
resource.getContents().add( forum );
try {
resource.save(Collections.EMPTY_MAP);
} catch (IOException e) {
e.printStackTrace();
}
|
在本例中,我們給 URI.createFileURI() 提供了希望保存成的文件名與目標(biāo)格式。這個(gè)例子因?yàn)槭潜4鏋?XMI,所以使用了 XMIResourceFactoryImpl 。一旦創(chuàng)建完畢,所有的模型對(duì)象就如我們所愿的持久保存起來了。在這個(gè)例子中,除 Forum 之外的每一個(gè)對(duì)象都被另一個(gè)類包含,所以我們只需要對(duì)包含所有孩子的 root 增加這條命令即可。如果某些對(duì)象沒有 包含 關(guān)系,那么也必須通過 resource.getContents().add() 顯式地將它們加進(jìn)去。否則,當(dāng)您調(diào)用 resource.save() 時(shí)就會(huì)出現(xiàn)異常。
|