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
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: neithereTypenoreGenericTypeare marked as derived, but depending on the situation, they one of them will be derived from the other.Specifically, if you set the
eTypeof anETypedElement, this implicitly creates a neweGenericType. Similarly, if you set theeGenericType, this will implicitly set theeTypeto be the erasure of this generic type.Unfortunately, this behaviour confuses
EcoreUtil.Copierwhen anETypedElementhas had itseTypeset explicitly. In this situation, the following happens:EcoreUtil.Copier.copycreates a new instance of theETypedElementand then starts copying all of its features.When it gets to the
eTypefeature, it doesn't copy it at this point, becauseeTypeis 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 originalETypedElement. ForeGenericTypeandeType, this check has been customised to make sure only one of the two actually returnstrue. As a result, for ourETypedElement,isSetEType()returnstrueandisSetEGenericType()returnstrue. Therefore,copyContainment()decides there is nothing to copy and moves on.By the time
copy()orcopyAll()returns, neither theeTypenor theeGenericTypehave been set for the newly created object. We now callcopyReferences().This will eventually attempt to copy the
eTypereference (remember that this is marked as not a containment reference).isSetType()returnstrue, socopyReference()goes on and copies the type information across. The setter foreTypein the copied object then creates the new instance ofEGenericType, butEcoreUtil.Copiernever gets to see it.Thus, if I want to get the original
EGenericTypeinstance and its copy to show up in the copy map, I will need to sub-classEcoreUtil.Copierand overridecopyReference()orcopyContainment()to handle this special case.