Eclipse EMF: customizing XML deserialization, so the old project can be loaded into a modified model

Situation

I have an RCP Eclipse application that manages application projects in the EMF model.

These projects are saved by serializing them in XMI format. Such files can then be loaded back into the model. To do this, I use standard EMF tools (such as Resource).

Because of model refactoring, the following has changed:

  • Old model
    • EClass MyClass with the Name attribute (with a capital letter).
    • XMI: <MyClass Name="My Class Name 1" ... />

against.

  • New model
    • EClass MyClass inherits from MyBaseClass , with the Name attribute (without a capital letter).
    • The EClass MyClass class no longer has a Name attribute, since EMF does not allow both. This makes sense as it will collide, for example, the getName() getter method.

Problem

How to load an old XMI project project into my new model?

Prior to this problem, I was able to:

  • avoid model modification
  • grow a model containing both old and new structures, and perform a modification after loading the project file: moving information from the old to the new, updating links, ....

In this case, however, I cannot load the XMI file in the first place: the model skips the Name attribute on the one hand and does not recognize (and thus ignores) the Name attribute on the other.

Question

What is the right place to implement this backward compatibility?

I guess I should work on the deserialization process or XML rendering.

Limitations for the solution:

  • New projects (containing <MyClass name="..." ... /> ) must also be loaded correctly.
  • Saving (i.e. serialization) of the project model should always occur in a new format!
+5
source share
1 answer

This question has been resolved by Ed Merks in the EMF forums .

Background

The cleanest way to support backward compatibility by intercepting XML mapping is to enable the ExtendedMetaData implementation in your EMF resource. This class is the central entry point for configuring EMF resources and their contents. This avoids the specialization of the various classes within the EMF.

However, my project already had a specialized XMLHelper class that handles XML serialization / deserialization, so Ed Mercks helped solve my problem inside this class.

Note that the XMI XMLHelperImpl source code shows how the ExtendedMetaData tool is called when it is enabled on the resource!

Custom XMLHelper

 /** * Helper class that allows intercepting the XML to model mapping, to support backwards compatibility. * <p> * 2 methods must be overridden to handle compatibility mappings: * <dl> * <dt>{@link XMLHelperImpl#getFeature(EClass, String, String, boolean)}</dt> * <dd>Is called to map features of a certain EClass. These include attributes and child elements in the XML file.</dd> * <dt>{@link XMLHelperImpl#getType(EFactory, String)}</dt> * <dd>Is called to map types that are used in the model.</dd> * </dl> * <p> * Their difference becomes clear by looking at the model file. Sometimes both need to be handled. For example: * <ul> * <li>a {@link Person} has zero or more {@link Person#getPhoneNumber()} configurations ('feature')</li> * <li>these features are of type {@link PhoneNumber} or possibly a subclass! ('type')</li> * </ul> * <p> * See https://www.eclipse.org/forums/index.php/m/1449615/ */ public class CustomXmlHelper extends XMLHelperImpl implements XMLHelper { public CustomXmlHelper() { super(); deresolve = true; } public CustomXmlHelper(XMLResource resource) { super(resource); deresolve = true; } @Override public EStructuralFeature getFeature(EClass eClass, String namespaceURI, String name, boolean isElement) { String compatName = name; if (eClass == ProjectModelPackage.Literals.MyClass) { if (!isElement && "Name".equals(name)) { // 1.x to 2.x compatibility (October 2014) // 1.x = MyClass attribute 'Name' // 2.x = MyBaseClass attribute 'name', shared by MyClass compatName = ProjectModelPackage.Literals.EMY_BASE_CLASS__NAME.getName(); // 'n(!)ame' } } // future feature mappings handled here return super.getFeature(eClass, namespaceURI, compatName, isElement); } @Override public EClassifier getType(EFactory eFactory, String name) { String compatName = name; if (eFactory == ProjectModelPackage.eINSTANCE) { // placeholder for type compatibility // if ("OldTypeName".equals(name)) { // compatName = ProjectModelPackage.Literals.NEW_TYPE_NAME.getName(); // } } return super.getType(eFactory, compatName); } } 
+3
source

Source: https://habr.com/ru/post/1205165/


All Articles