[zt]浅析Eclipse建模框架(EMF)及其动态能力

FROM:http://www.devx.com/Java/Article/29093/0/page/1

he Eclipse Modeling Framework (EMF) is a Java open source framework and code-generation facility for building tools and other applications based on a structured model. While the Eclipse Platform provides a powerful integration framework at the UI and file level, EMF enhances this capability to enable fine-grained data sharing among tools and applications.

Similar to other Java binding frameworks, e.g. JAXB or XMLBeans, given a model, EMF can generate Java source code that will allow you to create, query, update, deserialize, and serialize instances of your models. While the majority of Java binding frameworks support just one class of models, for example XML Schema, EMF supports generating code from XML Schema, UML class diagrams (Rational Rose or UML2), and annotated Java interfaces. In addition to the model code, EMF can also generate a complete application that includes a customizable editor.

The EMF-generated code has a built-in change notification mechanism and supports cross-document references. EMF provides a reflective API to access instances of your models and allows you to dynamically create models. EMF supports validation of model constraints. EMF provides powerful code generation tools that support regeneration of models and merging with user written code.

In this article, we'll explain just what the EMF is, take a look at the basic architecture.

EMF started out as an implementation of the Object Management Group's (OMG) Meta Object Facility (MOF) specification, which standardizes a metamodel for object oriented analysis and design. Over time, EMF was used to implement a large set of tools and thus evolved into an efficient Java implementation of a core subset of the MOF API.

The MOF-like core metamodel (model of a model) in EMF is called Ecore. In the current proposal for MOF 2.0, there is a similar subset of the MOF model, called Essential MOF (EMOF), which is separated out. There are small, mostly naming differences between Ecore and EMOF and therefore EMF can transparently read and write serializations of EMOF, allowing standard interchange of data between tools.

Today EMF's use is widespread. For example, EMF is used to implement the open source XML Schema Infoset Model (XSD), Service Data Objects (SDO), UML2, and Web Tools Platform (WTP) projects at Eclipse. In addition EMF is used in commercial products, such as Omondo EclipseUML and IBM Rational and WebSphere products.

Ecore and the Reflective API
One of the key interfaces in EMF is EObject, which is conceptually equivalent to java.lang.Object. All modeled objects, generated or not, implement this interface in order to provide several important features:

  • Similarly to Java's Object.getClass(), using the eClass() method, you can retrieve the metadata of the instance, i.e., its EClass.
  • On any EMF modeled object you can use the reflective API (eGet(), eSet()) to access its data. This is conceptually equivalent to Java's java.lang.reflect.Method.invoke() method, though much more efficient.
  • From any instance object you can get its container (parent) using the eContainer() method.
  • EObject also extends Notifier, which allows you to monitor all changes to the object's data.

 

As mentioned previously, EMF has its own simple metadata called Ecore. Figure 1 shows the complete class hierarchy of the Ecore metadata. In Figure 1, you can see that EPackage contains information about model classes (EClass) and data types (EDataType). EClass represents a modeled class, and specifies the attributes and references representing the data of instances. EAttribute represents simple data, specified by an EDataType. EReference represents an association between classes; its type is an EClass. EFactory contains methods to create model elements.

To find out more about EMF and Ecore please read the online overview or purchase the Eclipse Modeling Framework (EMF). The EMF Web site has several documents describing how to generate Java code from an XML Schema or UML diagram using EMF.

The next section describes an example which uses Ecore to create a simple model for companies, and then uses dynamic EMF to create, serialize and deserialize instances of this model. If you wish to follow along and you are already an Eclipse user, download and install the EMF 2.1 SDK or any newer version available on the EMF download site. Otherwise, you can download the standalone package, which includes EMF jars that do not have any dependencies on Eclipse and can be used in a standalone application.

Using Dynamic EMF Capabilities
In general, if you have models at development time, it is typically best to generate Java code because in this case your application will use less memory and provide faster access to data (using either the generated or the reflective API). While generating Java code serves a need for most applications, this might not always be the case. You might need to process data without requiring the availability of generated implementation classes. For example, you might not know at development time the model of the data you will be processing, making generated Java code a poor option

Dynamic, i.e., non-generated, classes can be created at runtime in several ways. Let's start by creating a company model programmatically using the Ecore API. The company model describes a company that has a name and departments. Each department is identified by a number and has employees. Each employee has a name. The code below shows an Ecore metamodel that corresponds to this model.

java 代码
EcoreFactory ecoreFactory = EcoreFactory.eINSTANCE;    EcorePackage ecorePackage = EcorePackage.eINSTANCE;       // create an Company class    EClass companyClass = ecoreFactory.createEClass();    companyClass.setName("Company");       // create company name    EAttribute companyName = ecoreFactory.createEAttribute();    companyName.setName("name");    companyName.setEType(ecorePackage.getEString());    companyClass.getEStructuralFeatures().add(companyName);       //create an Employee class    EClass employeeClass = ecoreFactory.createEClass();    employeeClass.setName("Employee");       //add a name attribute to an Employee class    EAttribute employeeName = ecoreFactory.createEAttribute();    employeeName.setName("name");    employeeName.setEType(ecorePackage.getEString());    employeeClass.getEStructuralFeatures().add(employeeName);       //create a Department class    EClass departmentClass = ecoreFactory.createEClass();    departmentClass.setName("Department");       //add department identification number    EAttribute departmentNumber = ecoreFactory.createEAttribute();    departmentNumber.setName("number");    departmentNumber.setEType(ecorePackage.getEInt());    departmentClass.getEStructuralFeatures().add(departmentNumber);       //department class can contain reference to one or many employees    EReference departmentEmployees = ecoreFactory.createEReference();    departmentEmployees.setName("employees");    departmentEmployees.setEType(employeeClass);       // specify that it could be one or more employees    departmentEmployees.setUpperBound(ETypedElement.UNBOUNDED_MULTIPLICITY);    departmentEmployees.setContainment(true);    departmentClass.getEStructuralFeatures().add(departmentEmployees);       // company can contain reference to one or more departments    EReference companyDepartments = ecoreFactory.createEReference();    companyDepartments.setName("department");    companyDepartments.setEType(departmentClass);    companyDepartments.setUpperBound(ETypedElement.UNBOUNDED_MULTIPLICITY);    companyDepartments.setContainment(true);    companyClass.getEStructuralFeatures().add(companyDepartments);       //create a package that represents company    EPackage companyPackage = ecoreFactory.createEPackage();    companyPackage.setName("company");    companyPackage.setNsPrefix("company");    companyPackage.setNsURI("http:///com.example.company.ecore");    companyPackage.getEClassifiers().add(employeeClass);    companyPackage.getEClassifiers().add(departmentClass);    companyPackage.getEClassifiers().add(companyClass);   

 

Using the reflective API you can create and initialize an instance of your model:

java 代码
// get company factory    EFactory companyFactory = companyPackage.getEFactoryInstance();       // using the factory create instance of company class and     // set company name    EObject company = companyFactory.create(companyClass);    company.eSet(companyName, "MyCompany");       // create an instance of employee class    EObject employee = companyFactory.create(employeeClass);    //using reflective API initialize name of employee    employee.eSet(employeeName, "John");       // create an instance of department class    EObject department = companyFactory.create(departmentClass);    department.eSet(departmentNumber, new Integer(123));    //add "John" to department    ((List)department.eGet(departmentEmployees)).add(employee);    // add the department to the company    ((List)company.eGet(companyDepartments)).add(department);  

 

Serializing and Deserializing Data
To serialize your model instances, you need to put a root object of your instance model into a resource. The EMF org.eclipse.emf.ecore.resource.Resource interface represents a physical storage location (such as file or URL) and provides methods to serialize and load data. Each resource is stored in a ResourceSet, which represents a collection of resources that have been created and loaded together, allowing for references among them. In particular, a ResourceSet keeps track of which resources have been loaded and makes sure that no resource in a ResourceSet is loaded twice.

Because EMF is capable of dealing with multiple model sources, e.g., XML Schema, it is also important to specify which resource implementation should be used for (de)serializing your data. Normally, when you invoke the ResourceSet.createResource(URI) method, it queries the Resource.Factory.Registry to look up a factory that is registered for that URI and uses it to create an appropriate resource implementation. Therefore, before you (de)serialize your data ensure that you register the appropriate resource factory implementation. EMF provides several Resource.Factory implementations:

For XML data use org.eclipse.emf.ecore.xmi.impl.XMLResourceFactoryImpl. For XMI data use org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl. For Ecore models use org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl.

With these EMF resources in your toolbox, you can use this code to serialize your data:

java 代码
// create resource set and resource     ResourceSet resourceSet = new ResourceSetImpl();       // Register XML resource factory    resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xmi",     new XMIResourceFactoryImpl());       Resource resource = resourceSet.createResource(URI.createFileURI("c:/temp/company.xmi"));    // add the root object to the resource    resource.getContents().add(company);    // serialize resource – you can specify also serialization    // options which defined on org.eclipse.emf.ecore.xmi.XMIResource    resource.save(null);  

 

The serialized form of the company.xmi is:

xml 代码
<?xml version="1.0" encoding="ASCII"?>   <company:Company xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"   xmlns:company="http:///com.example.company.ecore" name="MyCompany">     <department number="123">       <employees name="John"/>     </department>   </company:Company>  

 

During deserialization, the namespace URIs of XML data are used to locate the required Ecore packages (which describe the model for your instance documents). Therefore, before you attempt to load any model, ensure that you register the namespace URI for each Ecore package your documents will be using:

java 代码
// register package in local resource registry    resourceSet.getPackageRegistry().put(companyPackage.getNsURI(), companyPackage);    // load resource     resource.load(null);   

 

It is also important to notice the difference between local and global package (EPackage.Registry.INSTANCE) and resource factory (Resource.Factory.Registry.INSTANCE) registries. The global registry is static and therefore any application during lifetime of JVM can access the global registry and possibly overwrite it. To ensure that your registrations do not overwrite global registrations and vice versa, it is typically better to use local resource set registry.

Generating Dynamic Ecore from XML Schema
As mentioned previously, if your model is an XML Schema but you choose not to generate Java classes, you can dynamically create an Ecore model using the XSDEcoreBuilder. This example uses ipo.xsd:

java 代码
XSDEcoreBuilder xsdEcoreBuilder = new XSDEcoreBuilder();    ResourceSet resourceSet = new ResourceSetImpl();    Collection eCorePackages =     xsdEcoreBuilder.generate(URI.createFileURI("c:/temp/ipo.xsd"));      

The generate method returns Ecore packages that are created for each URI in this schema. If the schema imports other namespaces, more than one Ecore package is returned. Each package is registered locally in the resource set used to convert schema. Therefore, if you use the same resource set to load your instance XML document, you don't need to register the packages yourself.

Because XML Schema includes more concepts than Ecore, for example wildcards, EMF uses Ecore EAnnotations to record the mapping to XML Schema. During (de) serialization of the data EMF needs to process these annotations. To ensure these annotations are respected during (de) serialization, you must use the XMLResource.ExtendedMetaData option:

java 代码
HashMap options = new HashMap();    options.put(XMLResource.OPTION_EXTENDED_META_DATA, Boolean.TRUE);    // refer http://www.w3.org/TR/2004/PER-xmlschema-0-20040318/#ipo.xml    Resource resource = resourceSet.createResource(URI.createFileURI("c:/temp/ipo.xml"));    resource.load(options);  

EMF 2.1 also adds a new capability that allows you to convert schemas to Ecore on the fly while loading an XML document that contains an xsi:schemaLocation or xsi:noNamespaceSchemaLocation attribute. It also allows you to load an XML document that has no schema associated with it. To use this functionality you need to register org.eclipse.emf.ecore.xmi.impl.GenericXMLResourceFactoryImpl:

java 代码
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xml",     new GenericXMLResourceFactoryImpl());   

This article gave you a short introduction to EMF, explaining the core EMF concepts, and provided useful examples on how to exploit the dynamic capabilities of EMF.

Note: The opinions expressed in this paper are those of the authors, not of the IBM Corporation. IBM, alphaWorks, developerWorks, and WebSphere are trademarks of International Business Machines Corporation in the United States, other countries, or both. Rational is a registered trademark of International Business Machines Corporation and Rational Software Corporation, in the United States, other countries or both. Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. Other company, product and service names may be trademarks or service marks of others.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值