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.
EcoreFactory ecoreFactory = EcoreFactory.eINSTANCE;
EcorePackage ecorePackage = EcorePackage.eINSTANCE;
// create an Company class
EClass companyClass = ecoreFactory.createEClass();
// create company name
EAttribute companyName = ecoreFactory.createEAttribute();
//create an Employee class
EClass employeeClass = ecoreFactory.createEClass();
//add a name attribute to an Employee class
EAttribute employeeName = ecoreFactory.createEAttribute();
//create a Department class
EClass departmentClass = ecoreFactory.createEClass();
//add department identification number
EAttribute departmentNumber = ecoreFactory.createEAttribute();
//department class can contain reference to one or many employees
EReference departmentEmployees = ecoreFactory.createEReference();
// specify that it could be one or more employees
// company can contain reference to one or more departments
EReference companyDepartments = ecoreFactory.createEReference();
//create a package that represents company
EPackage companyPackage = ecoreFactory.createEPackage();
Using the reflective API you can create and initialize an instance of your model:
// get company factory
EFactory companyFactory = companyPackage.getEFactoryInstance();
// using the factory create instance of company class and
// set company name
EObject company = companyFactory.create(companyClass);
// create an instance of employee class
EObject employee = companyFactory.create(employeeClass);
//using reflective API initialize name of employee
// create an instance of department class
EObject department = companyFactory.create(departmentClass);
department.eSet(departmentNumber, new Integer(123));
//add "John" to department
// add the department to the company
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:
// create resource set and resource
ResourceSet resourceSet = new ResourceSetImpl();
// Register XML resource factory
Resource resource = resourceSet.createResource(URI.createFileURI("c:/temp/company.xmi"));
// add the root object to the resource
// serialize resource – you can specify also serialization
// options which defined on org.eclipse.emf.ecore.xmi.XMIResource
The serialized form of the company.xmi
<?xml version="1.0" encoding="ASCII"?>
<company:Company xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
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:
// register package in local resource registry
// load resource
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.