Skip to content

Commit

Permalink
adapt DefaultStateBasedChangeResolutionStrategy to return changes w…
Browse files Browse the repository at this point in the history
…ith elements of the actually provided resource set instead of ones from the internally used resource set
  • Loading branch information
Jan Wittler committed Dec 13, 2022
1 parent e33507d commit f751f99
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
package tools.vitruv.framework.views.changederivation

import java.util.HashMap
import java.util.Map
import org.eclipse.emf.common.notify.Notifier
import org.eclipse.emf.common.util.BasicMonitor
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.compare.EMFCompare
import org.eclipse.emf.compare.match.impl.MatchEngineFactoryImpl
import org.eclipse.emf.compare.match.impl.MatchEngineFactoryRegistryImpl
import org.eclipse.emf.compare.merge.BatchMerger
import org.eclipse.emf.compare.merge.IMerger
import org.eclipse.emf.compare.scope.DefaultComparisonScope
import org.eclipse.emf.compare.utils.UseIdentifiers
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl
import org.eclipse.emf.ecore.util.EcoreUtil
import tools.vitruv.change.atomic.eobject.CreateEObject
import tools.vitruv.change.atomic.eobject.EObjectAddedEChange
import tools.vitruv.change.atomic.eobject.EObjectExistenceEChange
import tools.vitruv.change.atomic.eobject.EObjectSubtractedEChange
import tools.vitruv.change.atomic.feature.FeatureEChange
import tools.vitruv.change.composite.description.TransactionalChange
import tools.vitruv.change.composite.recording.ChangeRecorder
import tools.vitruv.framework.views.util.ResourceCopier

import static com.google.common.base.Preconditions.checkArgument
import static com.google.common.base.Preconditions.checkState
import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.isPathmap

import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceUtil.getReferencedProxies
import tools.vitruv.change.atomic.uuid.UuidResolver

/**
* This default strategy for diff based state changes uses EMFCompare to resolve a
* diff to a sequence of individual changes.
* @author Timur Saglam
*/
class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResolutionStrategy {
/** The identifier matching behavior used by this strategy */
Expand Down Expand Up @@ -51,21 +61,20 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol
resource.URI, String.join(", ", proxies.map[toString]))
}

override getChangeSequenceBetween(Resource newState, Resource oldState, UuidResolver uuidResolver) {
override getChangeSequenceBetween(Resource newState, Resource oldState) {
checkArgument(oldState !== null && newState !== null, "old state or new state must not be null!")
checkArgument(uuidResolver !== null, "uuid resolver must not be null!")
newState.checkNoProxies("new state")
oldState.checkNoProxies("old state")
val monitoredResourceSet = new ResourceSetImpl()
val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet)
val resolver = UuidResolver.create(monitoredResourceSet)
uuidResolver.resolveResource(oldState, currentStateCopy, resolver)
return currentStateCopy.record(resolver) [
val resourceCopier = new ResourceCopier()
val currentStateCopy = resourceCopier.copyViewResource(oldState, monitoredResourceSet)
val change = currentStateCopy.record [
if (oldState.URI != newState.URI) {
currentStateCopy.URI = newState.URI
}
compareStatesAndReplayChanges(newState, currentStateCopy)
]
return transformChangeToOriginalResourceSet(change, resourceCopier)
}

override getChangeSequenceForCreated(Resource newState) {
Expand All @@ -76,27 +85,27 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol
val monitoredResourceSet = new ResourceSetImpl()
val newResource = monitoredResourceSet.createResource(newState.URI)
newResource.contents.clear()
return newResource.record(UuidResolver.create(monitoredResourceSet)) [
val change = newResource.record [
newResource.contents += EcoreUtil.copyAll(newState.contents)
]
return transformChangeToOriginalResourceSet(change, new ResourceCopier());
}

override getChangeSequenceForDeleted(Resource oldState, UuidResolver uuidResolver) {
override getChangeSequenceForDeleted(Resource oldState) {
checkArgument(oldState !== null, "old state must not be null!")
checkArgument(uuidResolver !== null, "uuid resolver must not be null!")
oldState.checkNoProxies("old state")
// Setup resolver and copy state:
val monitoredResourceSet = new ResourceSetImpl()
val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet)
val resolver = UuidResolver.create(monitoredResourceSet)
uuidResolver.resolveResource(oldState, currentStateCopy, resolver)
return currentStateCopy.record(resolver) [
val resourceCopier = new ResourceCopier()
val currentStateCopy = resourceCopier.copyViewResource(oldState, monitoredResourceSet)
val change = currentStateCopy.record [
currentStateCopy.contents.clear()
]
return transformChangeToOriginalResourceSet(change, resourceCopier)
}

private def <T extends Notifier> record(Resource resource, UuidResolver uuidResolver, ()=>void function) {
try (val changeRecorder = new ChangeRecorder(resource.resourceSet, uuidResolver)) {
private def <T extends Notifier> record(Resource resource, ()=>void function) {
try (val changeRecorder = new ChangeRecorder(resource.resourceSet)) {
changeRecorder.beginRecording
changeRecorder.addToRecording(resource)
function.apply()
Expand All @@ -120,4 +129,61 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol
val merger = new BatchMerger(mergerRegistry)
merger.copyAllLeftToRight(differences, new BasicMonitor)
}

/**
* Replaces all elements in the change, which are in the {@code monitoredResourceSet},
* with their counterparts in the originally provided resource set.
*/
private def TransactionalChange transformChangeToOriginalResourceSet(TransactionalChange change, ResourceCopier copier) {
var Map<EObject, EObject> createdObjects = new HashMap()
for (eChange : change.EChanges) {
switch eChange {
CreateEObject<EObject>:
eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, true)
EObjectExistenceEChange<EObject>:
eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, false)
FeatureEChange<EObject,?>:
eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, false)
}
switch eChange {
EObjectSubtractedEChange<EObject>:
if (eChange.oldValue !== null) {
eChange.oldValue = getOriginalForCopy(copier, eChange.oldValue, createdObjects, false)
}
}
switch eChange {
EObjectAddedEChange<EObject>:
if (eChange.newValue !== null) {
eChange.newValue = getOriginalForCopy(copier, eChange.newValue, createdObjects, true)
}
}
}
return change
}

private def <T extends EObject> T getOriginalForCopy(ResourceCopier copier, T copy, Map<EObject, EObject> createdObjects, boolean isCreateChange) {
if (isReadOnlyEObject(copy)) {
return copy
}
var original = copier.getOriginalForCopy(copy)
if (original === null) {
original = createdObjects.get(copy)
if (original === null && isCreateChange) {
original = EcoreUtil.create(copy.eClass) as T
createdObjects.put(copy, original)
}
}
checkState(original !== null, "could not find original for %s", copy)
checkState(original.eClass == copy.eClass, "mismatching classes for original %s and copy %s", original, copy)
return original as T
}

private def isReadOnlyEObject(EObject eObject) {
return eObject.eResource() !== null && eObject.eResource().getURI() !== null
&& isReadOnlyUri(eObject.eResource().getURI());
}

private def isReadOnlyUri(URI uri) {
return isPathmap(uri) || uri.isArchive();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tools.vitruv.framework.views.changederivation

import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.emf.ecore.resource.ResourceSet
import tools.vitruv.change.composite.description.VitruviusChange

/**
Expand All @@ -10,22 +11,35 @@ import tools.vitruv.change.composite.description.VitruviusChange
interface StateBasedChangeResolutionStrategy {

/**
* Resolves the state-based delta of two resources and returns the correlating change sequences.
* Resolves the state-based delta of two resources and returns the correlating change sequence.
* Any element in the returned {@VitruviusChange} must be contained in the {@link ResourceSet} of the {@code oldState}
* except for newly created elements which are not contained in any resource. The {@code oldState} remains unmodified
* from calling this method. This implies that any element contained in the returned {@link VitruviusChange} is also based
* on the old state.
*
* @param newState is the new state of the resource, must not be <code>null</code> and must not contain proxies.
* @param oldState is the current or old state of the resource, must not be <code>null</code> and must not contain proxies.
* @return a {@link VitruviusChange} that contains the individual change sequence.
*/
def VitruviusChange getChangeSequenceBetween(Resource newState, Resource oldState)

/**
* Resolves the state-based delta for creating the given resource and returns the correlating change sequences.
* Resolves the state-based delta for creating the given resource and returns the correlating change sequence.
* Any element in the returned {@VitruviusChange} must be contained in the {@link ResourceSet} of the {@code oldState}
* except for newly created elements which are not contained in any resource. As the returned {@VitruviusChange} is not
* applied to any {@link ResourceSet}, its contained elements do not reflect the state of {@code newState}.
*
* @param newState is the new state of the resource, must not be <code>null</code> and must not contain proxies.
* @return a {@link VitruviusChange} that contains the individual change sequence.
*/
def VitruviusChange getChangeSequenceForCreated(Resource newState)

/**
* Resolves the state-based delta for deleting the given resource and returns the correlating change sequences.
* Resolves the state-based delta for deleting the given resource and returns the correlating change sequence.
* Any element in the returned {@VitruviusChange} must be contained in the {@link ResourceSet} of the {@code oldState}.
* The {@code oldState} remains unmodified from calling this method. This implies that any element contained in the
* returned {@link VitruviusChange} is also based on the old state.
*
* @param oldState is the new state of the resource, must not be <code>null</code> and must not contain proxies.
* @return a {@link VitruviusChange} that contains the individual change sequence.
*/
Expand Down

0 comments on commit f751f99

Please sign in to comment.