摘要:使用元數據包中包含了中每一個被建模類對應的接口。任何對象的元數據是使用的實現來表示的。加載模型的序列化形式是個在運行期間獲取元數據的有效方法。反射提供一個反射式,可以檢查對象的元數據以及一般地訪問和操縱數據。
使用元數據
Java包org.eclipse.emf.ecore中包含了Ecore中每一個被建模類對應的接口。任何EMF對象的元數據是使用Ecore的實現(implementation)來表示的。這些信息在運行期間(runtime)總是可獲取的,所以我們可以使用Ecore API來查詢模型的結構,還可以使用反射式EObject API來訪問和操縱實例中的數據。此外,僅通過實例化 Ecore,我們就可以在運行期間動態定義一個模型。
本章我們仔細研究EMF對象暴露的元數據,并且看看元數據是如何被反射和動態EMF機制所利用的。
在第10章中,EMF生成了一個包接口,它為定義在包中的類型提供了對元數據的簡單訪問。
這個生成的接口僅僅是個便利:它提供的所有訪問也可以在基本接口中使用泛型方法(generic methods)。例如,接口EPO2Package,它從ExtendedPO2模型中生成,它提供了有個便捷的方法來訪問PurchaseOrder類。我們可以像這樣使用它:
EClass poClass = epo2Package.getPurchaseOrder();
不使用這個生成類,我們可以使用EPackage接口中的getEClassifier方法來簡單檢索類,如下:
EClass poClass = (EClass)epo2Package.getEClassifier("PurchaseOrder");
我們可以類似地訪問生成的包提供的屬性,引用以及其他所有內容。
加載模型的序列化形式是個在運行期間獲取元數據的有效方法。事實上,這也是在內存中創建模型的最初方法。但是,從磁盤中讀取,解析XML,構建整個模型是非常昂貴的操作,所以對于給定的模型,我們應該只做一次。
我們希望可以檢查兩個EMF對象是否是同一個類的實例。
包注冊表( package registry)提供對含有元數據的包的訪問,不管這些包是如何被初始化的。
使用java.util.Map填充包注冊表。鍵(key)是String類型命名空間URI,EPackage是值(value)。一些值也可能是EPackage的實例。
使用注冊表鍵入的getEPackage()方法來注冊生成的包。
當EMF獨立運行時,生成的包通常是在構建的時候注冊的,如下:
EPO2Package epo2Package = EPO2Package.eINSTANCE;
注冊包還有其他兩種通常的機制:URIConverters和xsi:schemaLocation,它們與沒有生成代碼的模型一起使用。包從它們的序列化形式中加載并自動注冊。
一旦包被注冊,就可以在任何需要它的時候輕松訪問:在合適的 EPackage.Registry上調用getEPackage()方法。例如,基于名稱和包的命名空間URI,使用全局包注冊表來獲取特定EClassifier,如下:
public static EClassifier getEClassifier(String nsURI, String name) { EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI); return ePackage == null ? null : ePackage.getEClassifier(name); }
我們也可以使用這個方法來從ExtendedPO2模型中獲取特定的類,如下:
EClassifier poClass = getEClassifier( "http://www.example.com/epo2.ecore","PurchaseOrder"));
回顧EPackage.Registry接口,它包含一個getEFactory() 方法,這個方法可以獲取已注冊的包的工廠(factory)。回顧第五章,每個EPackage總有一個關聯的EFactory,這個EFactory可以被用來初始化定義在包中的分類器(classifiers)。getEFactory() 方法可以作為獲取包以及調用包的getEFactoryInstance() 的一種捷徑。
EMF提供一個反射式API,可以檢查對象的元數據以及一般地訪問和操縱數據。EMF反射類似于Java反射,但是EMF反射運行于稍微高一點的層次。
創建對象EMF對象是使用工廠來創建的。例子如下:
PurchaseOrder order = epo2Factory.createPurchaseOrder();
生成的工廠擴展了基礎的工廠接口,EFactory繼承了可以反射式創建對象的通用的create() 方法。這種方法使用一個EClass來指定哪個類應該被實例化:
EClass poClass = ... EFactory epo2factory = poClass.getEPackage().getEFactoryInstance(); EObject order = epo2factory.create(poClass);
可以看出創建一個對象,所需要的僅是一個EClass。從其中我們可以輕松地獲取它包含的包,以及對應的工廠。事實上,有個捷徑,我們可以使用一個EMF多功能的方法來實現:
EObject order = EcoreUtil.create(poClass);
我們如何讓一個EClass用于創建對象?根本上,它來自于包。回顧上一章從注冊表中訪問包。首先,基于命名空間URI來檢索包,然后從包中獲取命名的類,最后,反射地實例化這個類:
public static EObject createEObject(String nsURI, String name) { EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI); EClass eClass = (EClass)ePackage.getEClassifier(name); return ePackage.getEFactoryInstance().create(eClass); }
我們現在可以創建一個PurchaseOrder類的實例:
EObject order = createEObject("http://www.example.com/epo2.ecore","PurchaseOrder");
這實際上與EMF的XML資源實現(XML resource implementation)在加載過程中必須在基于命名空間限定類型名稱來創建對象時非常相似。
反射的對象創建總是留給我們一個EObject的實例,EMF的用于建模對象的高性能基礎接口。接下來,我們就可以使用這個接口來檢索對象的元數據以及對其特性(features)的一般性訪問。
既然我們現在知道了如何去檢查元數據以及創建對象,接下來就只需要一種方式來反射地訪問屬性(attribute)和引用(reference)。這就用到了反射的 EObject API。
我們通常訪問元數據使用的是EObject的eClass()方法,其返回的是描述對象的EClass。然后我們可以使用元數據,通過反射性eGet(), eSet(), eIsSet(), eUnset() 方法來訪問和設置對象屬性和引用的值。這些方法每個都有EStructuralFeature來識別將被訪問的特性(feature)。
下面是打印出任意一個對象的名稱以及所有屬性的值的簡單方法:
public static void printAttributeValues(EObject object) { EClass eClass = object.eClass(); System.out.println(eClass.getName()); for (Iterator iter = eClass.getEAllAttributes().iterator(); iter.hasNext(); ) { EAttribute attribute = (EAttribute)iter.next(); Object value = object.eGet(attribute); System.out.print(" " + attribute.getName() + ": " + value); if (object.eIsSet(attribute)) { System.out.println(); } else { System.out.println(" (default)"); } } }
getEAllAttributes()方法返回所有的屬性。對于每個屬性,調用反射性eGet()方法來檢索屬性的值。我們也調用eIsSet()來決定是否將額外的信息“(default)”附加到打印的值后面。對于Item的實例,輸出如下:
Item productName: Tire quantity: 4 USPrice: 50 comment: null (default) shipDate: null (default) partNum: 622-RT
接下來看一個修改數據的例子,尋找一個名稱為"USPrice"的整型特性,把其現有的值減少10%,如下:
public static void adjustPrice(EObject object) { EStructuralFeature feature =object.eClass().getEStructuralFeature("USPrice"); if (feature != null &&feature.getEType() == EcorePackage.Literals.EINT) { int price = ((Integer)object.eGet(feature)).intValue(); object.eSet(feature, new Integer(price * 9 / 10)); } }
Ecore的EInt數據類型代表了原始(primitive) Java類型int,所以我們仍然必須使用帶有eGet()和eSet()整型封裝類。
除了值的類型總是EObject之外,在對象中操縱引用(reference)與檢索屬性值類似。EObject也有其他方法可以幫忙,例如,如果想要尋找對象的容器(container),你就傾向于編寫方法findContainer(),它可以遍歷所有的對象引用,并檢查isContainer(),最后返回第一個非null的值:
public static EObject findContainer(EObject object) { for (Iterator i = object.eClass().getEAllReferences().iterator();i.hasNext(); ) { EReference reference = (EReference)i.next(); if (reference.isContainer()) { EObject value = (EObject)object.eGet(reference); if (value != null) { return value; } } } return null; }
EObject總是知道自己的容器,并且通過eContainer()方法使其可用。如果這個方法返回的是null,那么這個對象就沒有被包含。
類似地,你可能想獲取某個對象包含的所有對象。當然也可以使用上面類似地方法,然而,有個更便捷的方式來實現:使用EObject API eContents()。它可以返回一個所有對象包含的引用的只讀列表(list)視圖,此外,eCrossReferences() 返回的是非包含的引用的視圖。
我們可以使用eContents()來建立價格調整的例子:
public static void adjustPrices(EObject object) { adjustPrice(object); for (Iterator i = object.eContents().iterator(); i.hasNext(); ) { adjustPrices((EObject)i.next()); } }
這個例子使用了遞歸訪問的方法,如果使用eAllContents()就可以消除遞歸:
public static void adjustPrices(EObject object) { adjustPrice(object); for (Iterator i = object.eAllContents(); i.hasNext(); ) { adjustPrice((EObject)i.next()); // No recursion! } }
(可以再補充抽象類相關)
動態EMF(Dynamic EMF) 生成的代碼也可以看作是類型安全(type-safe) API,而反射性EObject API的動態實現( dynamic implementation),可以看做是類型指定(type-specific) API。相比于動態實現,生成的代碼要更高效且使用更少的內存,以及提供更快的數據訪問。然而,如果你不需要提供一個類型安全API或者修改默認的行為(behavior),動態實現則是更好的選擇,它允許模型能被更自由地共享,以及在不維護生成的類的情況下進行演化。這就是動態和靜態實現之間的正常權衡(trade-off)。
同時兼得代碼生成的高性能和動態EMF的靈活性的一種可能的方法是,在運行期間調用代碼生成器,然后動態地加載生成的類。
(JDT JIT 以及其他未完待續)
(與第8章和第9章相關)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67473.html
摘要:定義模型元模型用于表示中模型的模型稱為。用于表示的類型,它可以是基本類型,例如或對象類型等。此外,因為是貨物的容器并會在其中將貨物作為孩子序列化,所以需要標識出。 EMF介紹 為了理解EMF究竟是什么,你只需要知道一件事:模型(model)是什么?模型的目的是什么? EMF不要求全新的方法論亦或是任何復雜的建模工具。只需要從Eclipse的Java開發工具著手開始。 EMF將建模概念...
摘要:定義模型元模型用于表示中模型的模型稱為。用于表示的類型,它可以是基本類型,例如或對象類型等。此外,因為是貨物的容器并會在其中將貨物作為孩子序列化,所以需要標識出。 EMF介紹 為了理解EMF究竟是什么,你只需要知道一件事:模型(model)是什么?模型的目的是什么? EMF不要求全新的方法論亦或是任何復雜的建模工具。只需要從Eclipse的Java開發工具著手開始。 EMF將建模概念...
摘要:生成的包首次被訪問時,在全局包注冊表中自動地注冊。然而,類似于資源工廠注冊表,這種顯式注冊的過程僅當獨立運行時被要求,在下運行時通過擴展指針來自動地完成。通過使用合適的資源工廠,就可以確定被產生的和被使用的持久化形式。 持久化(Persistence) EMF擁有一個強大的模型持久化框架。通過一個高度可定制資源實現(resource implementation)來支持XML序列化...
摘要:生成的包首次被訪問時,在全局包注冊表中自動地注冊。然而,類似于資源工廠注冊表,這種顯式注冊的過程僅當獨立運行時被要求,在下運行時通過擴展指針來自動地完成。通過使用合適的資源工廠,就可以確定被產生的和被使用的持久化形式。 持久化(Persistence) EMF擁有一個強大的模型持久化框架。通過一個高度可定制資源實現(resource implementation)來支持XML序列化...
閱讀 3476·2021-11-25 09:43
閱讀 1082·2021-11-15 11:36
閱讀 3326·2021-11-11 16:54
閱讀 3993·2021-09-27 13:35
閱讀 4384·2021-09-10 11:23
閱讀 5954·2021-09-07 10:22
閱讀 3050·2021-09-04 16:40
閱讀 781·2021-08-03 14:03