Reputation: 41
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
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:
EcoreUtil.Copier.copy
creates a new instance of the ETypedElement
and then starts copying all of its features.
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).
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.
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()
.
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