Open HeikoKlare opened 2 years ago
When using a state-deriving view (in cases changes cannot be recorded), we use a StateBasedChangeResolutionStrategy
to derive the changes for the changed view. In its default implementation (DefaultStateBasedChangeResolutionStrategy
), we use EMF Compare to get the list of differences between the original and changed model and then replay those differences on an observed copy of the original model, thus being able to use our change recorder and convert the differences to our change format. Since the problematic references of UML are not marked as derived
, EMF Compare generated differences for them. However, trying to replay those differences results in the same problems as described in Issue 2.
The framework contains several workarounds to deal with metamodels that define custom behavior of specific kinds of
EReferences
, which are partly derived but not marked as such. Currently, this concerns the UML2 metamodel shipped with EMF.UML Metamodel Behavior
The UML metamodel implements some
EReferences
using some weird manually and partly derived collections. For example, theInterfaceRealization.clients
are a set of manually added elements combined with theInterfaceRealization.implementingClassifier
, which, in turn, is just theeContainer
of theInterfaceRealization
. This behavior is implemented by theSubsetSupersetEObjectResolvingEList
, which is used to implement theclients
reference ofInterfaceRealization
as can be seen in the implementation. These kinds of references partly behave as if there were derived, because changing some other reference (such as theimplementingClassifier
) also changes that reference (such asclients
), but they are not marked asderived
, in particular because they also contain manually added elements in addition to the derived elements. Theclients
case is only an example for a pattern used all over the references in the UML metamodel.Consequence: Ambiguous Changes
Using these kinds of
EReferences
leads to problematic change events. For example, setting theimplementingClassifier
also produces a change of theclients
. If you want to apply these changes to another instance of the same model, the change toclients
must not be applied, because setting theimplementingClassifier
already implicitly adds the element toclients
and otherwise the same element would be added intoclients
twice. But this only applies if the element to be added toclients
is also contained in another reference from which it is derived, but the change can also affect an element that was manually added to aclient
. These two situation can, however, not be distinguished.Issue 1: Copying Models
This behavior does not only affect our implementations but also basic EMF mechanisms. Our implementation for creating views uses the
EcoreUtil
functionality to copy modelsEcoreUtil$Copier
, which is not able to properly handle these partly derived references. In case of theclients
example, theimplementingClassifier
is contained twice in theclients
reference after using theEcoreUtil$Copier
, which is why we use a different mechanism for copying UML models in the view generation (see the implementation). This is, however, a UML-specific hack, which should actually not be embedded into the framework.Issue 2: Applying Recorded Changes
A similar problem is specific to our implementation of change recording. Our change recorder properly handles derived features, but due to such
EReferences
not being derived, it handles them as an ordinary reference. Just like theEcoreUtil$Copier
, it creates a change for theclients
modification and, if applying these changes, adds the element toclients
although it was already added when setting theimplementingClassifier
. When removing elements, it even leads to the situation that remove changes are generated for elements that have already implicitly been removed, such as removingimplementingClassifier
implicitly removing the one of theclients
but still producing a change that removes that client explicitly. To deal with that problem, we have a relaxed implementation of change application. In that implementation, we accept failures in change application, which should actually not occur and thus throw an exception but are a result of the UML metamodel references implementation and thus are only logged as a warning.Issue 3: Executing
EcoreUtil.delete
on UML elementsEven executing EMF-internal functionality provided by the
EcoreUtil
class does not work properly with the UML metamodel. Performing a delete on a UML element that is also contained in such a problematic reference least to an exception because removing it from one reference (either the actual one or the "derived" one) also removes it from the other and the later removal by thedelete
operation fails afterwards.Action Item
These problems with the UML metamodel lead to domain-specific workarounds in our framework, which should actually not be part of it. Although the best solution would be to somehow fix the UML metamodel, these problems may not be restricted to the UML metamodel, but custom implementations of metamodel behavior can also occur with other metamodels (see also our discussion in the PR introducing the copy workaround). Thus, we should consider providing some mechanism to dynamically attach specific functionality for specific kinds of resources where the ordinary functionality does work properly without implementing such domain-specific functionality in the framework. I do not have yet a concrete proposal though.