Steffen Zschaler
Steffen Zschaler

Reputation: 41

Copying EGenericType instances with ECoreUtil.Copier

I am working with EMF models and need to make copies of these. My models consist of interlinked instances of three meta-models, one of which is Ecore itself. So far, so good.

However, when it comes to copying these models (which I do using EcoreUtil.Copier.copyAll following the usual protocol), some parts of my model are not copied. Specifically, the Ecore instance contains a number of instances of EGenericType (because there are references and attributes and these are automatically set up with EGenericType instances to show their types). The result of copying contains everything, but these instances of EGenericType.

I have searched high and low and looked into the EMF source code, too, but couldn't figure out what the problem is. I have looked at the source of EcoreUtil.Copier and it checks for each structural feature whether it is changeable and not derived to decide whether to actually copy it. This condition is true for the reference to EGenericType, so this should be copied as a containment reference.

Interestingly, the result of the copy does contain copied instances of EGenericType in all the right places in the object graph. However, these are not mapped in the copier, so don't seem to have been created by a call to EcoreUtil.Copier.copy along the way, but implicitly.

Any ideas when these are created and how I might get them to show up in the copier map?

Many thanks,

Steffen

Upvotes: 2

Views: 295

Answers (1)

Steffen Zschaler
Steffen Zschaler

Reputation: 41

OK, so I have dug deeper into this with the debugger and think I now understand what's going on:

Essentially, ETypedElement (which is what contains types and generic types) is a bit loose with its contract: neither eType nor eGenericType are marked as derived, but depending on the situation, they one of them will be derived from the other.

Specifically, if you set the eType of an ETypedElement, this implicitly creates a new eGenericType. Similarly, if you set the eGenericType, this will implicitly set the eType to be the erasure of this generic type.

Unfortunately, this behaviour confuses EcoreUtil.Copier when an ETypedElement has had its eType set explicitly. In this situation, the following happens:

  1. EcoreUtil.Copier.copy creates a new instance of the ETypedElement and then starts copying all of its features.

  2. When it gets to the eType feature, it doesn't copy it at this point, because eType is not a containment reference (for obvious reasons).

  3. Next it comes to eGenericType, which is a containment reference. However, the first thing it does is to check whether this is set in the original ETypedElement. For eGenericType and eType, this check has been customised to make sure only one of the two actually returns true. As a result, for our ETypedElement, isSetEType() returns true and isSetEGenericType() returns true. Therefore, copyContainment() decides there is nothing to copy and moves on.

  4. By the time copy() or copyAll() returns, neither the eType nor the eGenericType have been set for the newly created object. We now call copyReferences().

  5. This will eventually attempt to copy the eType reference (remember that this is marked as not a containment reference). isSetType() returns true, so copyReference() goes on and copies the type information across. The setter for eType in the copied object then creates the new instance of EGenericType, but EcoreUtil.Copier never gets to see it.

Thus, if I want to get the original EGenericType instance and its copy to show up in the copy map, I will need to sub-class EcoreUtil.Copier and override copyReference() or copyContainment() to handle this special case.

Upvotes: 2

Related Questions