(此文大部分翻譯整理自Eclipse Modeling Framework: A Developer's Guild的13.2章)
在EMF Persistence API中主要涉及到4個接口Resource, ResourceSet, Resource.Factory以及URIconverter。雖然EMF提供了這些接口的缺省的XML序列化的實現(xiàn),但是也可以用這些API來實現(xiàn)其他的序列化方式,不論其是否是基于XML的,或者基于流的。
URI用來表示某一類型的數(shù)據(jù),由三個部分組成:scheme, scheme-specific part以及可選的fragment。EMF提供了自己的URI的實現(xiàn)(而沒有用JDK的URL):org.eclipse.emf.common.util.URI。
scheme部分表示了存取resource的協(xié)議,可以使標準的file,或者是jar。在eclipse中,使用platform來存取在workspace中的resource。例如:platform:/resource/project/po.xml。EMF也提供了EcoreUtil.getPlatformResourceMap(),來將一個platform的URI轉(zhuǎn)換成標準的基于本地文件系統(tǒng)file協(xié)議的URI。
scheme-specific part的解釋會根據(jù)具體的scheme不同而不同,但是在EMF中,使用了一種通用的層次格式,這種格式包括autority, device,以及一系列的segments。authority由//打頭。其它均由/打頭。
fragment表示一個resource內(nèi)部的的一個部分。使用#來同其他部分分離。例如:file:/c:/dir1/dir2/myfile.xml#loc。EMF使用帶有fragment的URI來對resource中的EObject進行引用。每一個EMF resource都有一個唯一的URI,而每一個resource中的對象,都有一個唯一的fragement來標志它。
URIConverter將一個輸入的URI轉(zhuǎn)換成一個resource的真實地URI。可以用來將一個namespace URI轉(zhuǎn)換成一個物理文件的URI,或者重定向到另外的一個老的URI上。
Resource表示一個EObject的序列化容器,其實際地址由其URI所指定。Resource接口最重要的方法是save(), load(),getEObject()以及getURIFragment()方法。save()和load()方法在ResourceImpl中并沒有具體的處理裝載與保存的實現(xiàn),具體的處理是由storage-specific的resource的子類完成的。
Resource的unload()方法在某些時候也會很有用。它會將Resource中的所有對象都轉(zhuǎn)換為代理對象,使得后續(xù)的調(diào)用變成ondemand的調(diào)用,這能夠讓你得到最新的數(shù)據(jù)。如果底層的文件發(fā)生了改變的話。
Resource的getEObject()方法能夠使用一個對象的fragment來存取一個EObject。例如:
Item item = (Item)resource.getEObject("http://@orders.0/@items.2");
要得到一個對象的fragment也很容易,使用getURIFragment()方法即可:
String fragment = resource.getURIFragment(item);
Resource.Factory是用來創(chuàng)建Resource的。Resource.Factory是由一個注冊庫Registry來管理,定位的。一個Resource.Factory對應于一類URI,而不是某一特定的URI。例如,缺省的registry允許你為一類的URI scheme或者extension注冊一個Resource.Factory。Resource.Factory可以通過一個定義在Resource.Factory內(nèi)部的Descriptor來進行注冊。Descriptor提供了創(chuàng)建Factory的方法。這里也是一個插件的擴展點,可以用來向系統(tǒng)中注冊新的Descriptor。
Registry可以用過其靜態(tài)的INSTANCE字段來訪問其一個實例,缺省實現(xiàn)是ResourceFactoryRegistryImpl。它首先會根據(jù)URI的scheme來檢查protocolToFactoryMap中的Factory,如果沒有找到,則使用URI的文件的擴展名來檢查extensionToFactoryMap中是否有,如果人染沒有找到,則查找extensionToFactoryMap的DEFAULT_EXTENSION(也就是*)。如果仍然沒有找到,則調(diào)用delegatedGetFactory(),允許你裝載一個自己的Factory Registry。當找到一個Descriptor之后,調(diào)用其createFactory()來創(chuàng)建一個Factory。
下面的這個擴展點是定義在org.eclipse.emf.ecore.xmi插件中的:
<extension point = "org.eclipse.emf.ecore.extension_parser">
<parser type="*"
class="org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl"/>
</extension>
可以看到,XMIResourceFactoryImpl被作為缺省的ResourceFactory注冊了,因此在沒有其它的Factory被注冊的時候,將缺省使用XMIResourceFactoryImpl。你也同樣可以創(chuàng)建新的Resource實現(xiàn),以及對應的Factory,并通過上面的擴展點來進行注冊。
當EMF運行在非Eclipse環(huán)境下時,缺省的擴展點沒有被注冊,則需要手工的注冊:
Resource.Factory.Registry.INSTANCE.
getExtensionToFactoryMap().put("*", new XMIResourceFactoryImpl());
Resource.Factory被ResourceSet所使用來創(chuàng)建Resource。
一個ResourceSet代表了一個Resource的集合。提供了createResource(),getResource(),以及getEObject()方法。createResource()創(chuàng)建一個新的,空的resource。getResource()方法也同樣創(chuàng)建一個resource,但是會使用給定的URI來裝載這個Resource。用戶應該始終調(diào)用ResourceSet的這兩個方法,而不是Resource的構(gòu)造函數(shù)或者Resource.Factory的createResource()方法來創(chuàng)建一個Resource。這是因為ResourceSet會保證相同的URI所對應的Resource不會被裝載多次,而導致內(nèi)存中有相同的副本,并且,ResourceSet能夠自動處理跨文檔的引用,而Resource卻不行。
EMF中資源的保存與讀取,可以通過下面的兩個簡單的代碼片斷來例示:
裝載:
ResourceSet resourceSet2 = new ResourceSetImpl();
URI fileURI2 = URI.createFileURI(filepath);
//Attention, The second parameter must be trur to get the resource for the first time.
Resource poResource2 = resourceSet2.getResource(fileURI2, true);
保存也很簡單:
URI fileURI = URI.createFileURI(filepath);
Resource poResource = resourceSet.createResource(fileURI);
poResource.getContents().add(model);
try {
poResource.save(null);
} catch (IOException e) {
assertTrue("IOException: " + e.getMessage(), false);
}