From d29c4faebbe95cccfa7932cec02b7a75b1cc7e4c Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Wed, 9 Nov 2022 11:34:45 +0100 Subject: [PATCH 01/19] adapt interfaces to UUID resolver --- .../vitruv/framework/views/ViewSource.java | 10 ++++++++ ...ltStateBasedChangeResolutionStrategy.xtend | 19 +++++++++------ .../StateBasedChangeResolutionStrategy.xtend | 7 ++++-- .../framework/views/impl/BasicView.xtend | 10 +++++--- .../views/impl/ChangeDerivingView.xtend | 4 ++-- .../views/impl/ChangeRecordingView.xtend | 2 +- .../framework/views/impl/ModifiableView.xtend | 3 ++- .../vsum/helper/VsumFileSystemLayout.xtend | 6 +++++ .../vsum/internal/InternalVirtualModel.xtend | 4 ++-- .../vsum/internal/ModelRepository.xtend | 6 +++++ .../vsum/internal/ResourceRepositoryImpl.java | 24 ++++++++++++++----- .../vsum/internal/VirtualModelImpl.java | 6 +++++ .../framework/vsum/VirtualModelTestUtil.xtend | 5 ++-- 13 files changed, 80 insertions(+), 26 deletions(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSource.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSource.java index c63ce19766..ba3a389131 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSource.java +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/ViewSource.java @@ -4,6 +4,8 @@ import org.eclipse.emf.ecore.resource.Resource; +import tools.vitruv.change.atomic.uuid.UuidResolver; + /** * A view source giving access to the underlying source models of the view. */ @@ -15,4 +17,12 @@ public interface ViewSource { * @return {@link Resource}s as the sources of a view */ Collection getViewSourceModels(); + + /** + * Returns the {@link UuidResolver} associated with the resources in this view + * source. + * + * @return the {@link UuidResolver} of this view source. + */ + public UuidResolver getUuidResolver(); } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend index 51c6e31f9e..1be044293f 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend @@ -18,6 +18,7 @@ import tools.vitruv.framework.views.util.ResourceCopier import static com.google.common.base.Preconditions.checkArgument 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 @@ -50,13 +51,15 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol resource.URI, String.join(", ", proxies.map[toString])) } - override getChangeSequenceBetween(Resource newState, Resource oldState) { + override getChangeSequenceBetween(Resource newState, Resource oldState, UuidResolver uuidResolver) { checkArgument(oldState !== null && newState !== null, "old state or new state must not be null!") newState.checkNoProxies("new state") oldState.checkNoProxies("old state") val monitoredResourceSet = new ResourceSetImpl() val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet) - return currentStateCopy.record [ + val resolver = UuidResolver.create(monitoredResourceSet) + uuidResolver.resolveResource(oldState, currentStateCopy, resolver) + return currentStateCopy.record(resolver) [ if (oldState.URI != newState.URI) { currentStateCopy.URI = newState.URI } @@ -72,24 +75,26 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol val monitoredResourceSet = new ResourceSetImpl() val newResource = monitoredResourceSet.createResource(newState.URI) newResource.contents.clear() - return newResource.record [ + return newResource.record(UuidResolver.create(monitoredResourceSet)) [ newResource.contents += EcoreUtil.copyAll(newState.contents) ] } - override getChangeSequenceForDeleted(Resource oldState) { + override getChangeSequenceForDeleted(Resource oldState, UuidResolver uuidResolver) { checkArgument(oldState !== null, "old state must not be null!") oldState.checkNoProxies("old state") // Setup resolver and copy state: val monitoredResourceSet = new ResourceSetImpl() val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet) - return currentStateCopy.record [ + val resolver = UuidResolver.create(monitoredResourceSet) + uuidResolver.resolveResource(oldState, currentStateCopy, resolver) + return currentStateCopy.record(resolver) [ currentStateCopy.contents.clear() ] } - private def record(Resource resource, ()=>void function) { - try (val changeRecorder = new ChangeRecorder(resource.resourceSet)) { + private def record(Resource resource, UuidResolver uuidResolver, ()=>void function) { + try (val changeRecorder = new ChangeRecorder(resource.resourceSet, uuidResolver)) { changeRecorder.beginRecording changeRecorder.addToRecording(resource) function.apply() diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend index 4ccd82a26c..4690b4e68a 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend @@ -2,6 +2,7 @@ package tools.vitruv.framework.views.changederivation import org.eclipse.emf.ecore.resource.Resource import tools.vitruv.change.composite.description.VitruviusChange +import tools.vitruv.change.atomic.uuid.UuidResolver /** * Strategy for resolving state-based changes to individual change sequences. @@ -13,9 +14,10 @@ interface StateBasedChangeResolutionStrategy { * Resolves the state-based delta of two resources and returns the correlating change sequences. * @param newState is the new state of the resource, must not be null and must not contain proxies. * @param oldState is the current or old state of the resource, must not be null and must not contain proxies. + * @param uuidResolver contains the UUIDs for the objects in oldState. * @return a {@link VitruviusChange} that contains the individual change sequence. */ - def VitruviusChange getChangeSequenceBetween(Resource newState, Resource oldState) + def VitruviusChange getChangeSequenceBetween(Resource newState, Resource oldState, UuidResolver uuidResolver) /** * Resolves the state-based delta for creating the given resource and returns the correlating change sequences. @@ -27,7 +29,8 @@ interface StateBasedChangeResolutionStrategy { /** * Resolves the state-based delta for deleting the given resource and returns the correlating change sequences. * @param oldState is the new state of the resource, must not be null and must not contain proxies. + * @param uuidResolver contains the UUIDs for the objects in oldState. * @return a {@link VitruviusChange} that contains the individual change sequence. */ - def VitruviusChange getChangeSequenceForDeleted(Resource oldState) + def VitruviusChange getChangeSequenceForDeleted(Resource oldState, UuidResolver uuidResolver) } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend index 83ac51cba4..c3d4074592 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend @@ -21,6 +21,7 @@ import static com.google.common.base.Preconditions.checkState import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories import tools.vitruv.change.composite.description.PropagatedChange import tools.vitruv.change.composite.description.VitruviusChange +import tools.vitruv.change.atomic.uuid.UuidResolver package class BasicView implements ModifiableView, ChangePropagationListener { @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) @@ -29,8 +30,10 @@ package class BasicView implements ModifiableView, ChangePropagationListener { var ViewCreatingViewType viewType @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) var ChangeableViewSource viewSource - @Accessors(PROTECTED_GETTER, PROTECTED_SETTER) + @Accessors(PROTECTED_GETTER) var ResourceSet viewResourceSet + @Accessors(PROTECTED_GETTER) + val UuidResolver uuidResolver boolean modelChanged @Accessors(PROTECTED_SETTER) boolean viewChanged @@ -46,6 +49,7 @@ package class BasicView implements ModifiableView, ChangePropagationListener { this.selection = selection viewSource.addChangePropagationListener(this) viewResourceSet = new ResourceSetImpl().withGlobalFactories + uuidResolver = UuidResolver.create(viewResourceSet) update } @@ -133,8 +137,8 @@ package class BasicView implements ModifiableView, ChangePropagationListener { ] } - override modifyContents((ResourceSet)=>void modificationFunction) { - modificationFunction.apply(viewResourceSet) + override modifyContents((ResourceSet, UuidResolver)=>void modificationFunction) { + modificationFunction.apply(viewResourceSet, uuidResolver) } override withChangeRecordingTrait() { diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend index 9fe06b01c7..4d0fc2e1ab 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend @@ -79,9 +79,9 @@ class ChangeDerivingView implements ModifiableView, CommittableView { if (referenceState === null) { return changeResolutionStrategy.getChangeSequenceForCreated(newState) } else if (newState === null) { - return changeResolutionStrategy.getChangeSequenceForDeleted(referenceState) + return changeResolutionStrategy.getChangeSequenceForDeleted(referenceState, view.uuidResolver) } else { - return changeResolutionStrategy.getChangeSequenceBetween(newState, referenceState) + return changeResolutionStrategy.getChangeSequenceBetween(newState, referenceState, view.uuidResolver) } } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend index ae45027945..158827536a 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend @@ -32,7 +32,7 @@ class ChangeRecordingView implements ModifiableView, CommittableView { } private def setupChangeRecorder() { - changeRecorder = new ChangeRecorder(view.viewResourceSet) + changeRecorder = new ChangeRecorder(view.viewResourceSet, view.uuidResolver) changeRecorder.addToRecording(view.viewResourceSet) changeRecorder.beginRecording() } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend index 2d667d3e5e..c64e6e180d 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend @@ -3,12 +3,13 @@ package tools.vitruv.framework.views.impl import org.eclipse.emf.ecore.resource.ResourceSet import tools.vitruv.framework.views.ChangeableViewSource import tools.vitruv.framework.views.View +import tools.vitruv.change.atomic.uuid.UuidResolver /** * A view whose contents can be modified, in particular by a view type implementation. */ interface ModifiableView extends View { - def void modifyContents((ResourceSet)=>void modificationFunction); + def void modifyContents((ResourceSet, UuidResolver)=>void modificationFunction); def ChangeableViewSource getViewSource() } diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend index e42eaf0c15..7752ff3bf4 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend @@ -13,6 +13,7 @@ import org.eclipse.emf.common.util.URI class VsumFileSystemLayout { static final String CORRESPONDENCES_FILE = "correspondences.correspondence"; + static final String UUIDS_FILE = "Uuid.uuid"; static final String MODELS_FILE = "models.models"; static final String VSUM_FOLDER_NAME = "vsum"; static final String CONSISTENCY_METADATA_FOLDER_NAME = "consistencymetadata"; @@ -69,6 +70,11 @@ class VsumFileSystemLayout { checkPrepared() return vsumFolder.resolve(CORRESPONDENCES_FILE).toFile.createFileURI() } + + def URI getUuidsURI() { + checkPrepared() + return vsumFolder.resolve(UUIDS_FILE).toFile.createFileURI() + } def Path getModelsNamesFilesPath() { checkPrepared() diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend index 0e487d3a66..0b3e6a6f8e 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/InternalVirtualModel.xtend @@ -1,10 +1,10 @@ package tools.vitruv.framework.vsum.internal import org.eclipse.emf.common.util.URI -import tools.vitruv.framework.views.ChangeableViewSource -import tools.vitruv.framework.vsum.VirtualModel import tools.vitruv.change.correspondence.Correspondence import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView +import tools.vitruv.framework.views.ChangeableViewSource +import tools.vitruv.framework.vsum.VirtualModel interface InternalVirtualModel extends VirtualModel, ChangeableViewSource { def EditableCorrespondenceModelView getCorrespondenceModel() diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend index 2903aff58b..de4db8de88 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend @@ -4,6 +4,7 @@ import org.eclipse.emf.common.util.URI import java.util.Collection import org.eclipse.emf.ecore.resource.Resource import tools.vitruv.change.propagation.ChangeRecordingModelRepository +import tools.vitruv.change.atomic.uuid.UuidResolver package interface ModelRepository extends ChangeRecordingModelRepository { /** @@ -17,4 +18,9 @@ package interface ModelRepository extends ChangeRecordingModelRepository { def void saveOrDeleteModels() def Collection getModelResources() + + /** + * Returns the {@link UuidResolver} associated with all resources in this repository. + */ + def UuidResolver getUuidResolver() } diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java index 31991b4563..c08a655bcf 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java @@ -13,6 +13,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -24,6 +25,7 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import tools.vitruv.change.atomic.uuid.UuidResolver; import tools.vitruv.change.composite.description.TransactionalChange; import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.change.composite.recording.ChangeRecorder; @@ -41,6 +43,7 @@ class ResourceRepositoryImpl implements ModelRepository { private final Map modelInstances = new HashMap<>(); private final FileExtensionRecorderMapping fileExtensionRecorderMapping = new FileExtensionRecorderMapping(); private final PersistableCorrespondenceModel correspondenceModel; + private final UuidResolver uuidResolver = UuidResolver.create(modelsResourceSet); private final VsumFileSystemLayout fileSystemLayout; @@ -70,12 +73,12 @@ ChangeRecorder getRecorder(String fileExtension) { return fileExtensionsToRecorder.get(fileExtensionToExtensionsSet.get(fileExtension)); } - void registerRecorder(Set fileExtensions, ResourceSet recorderResourceSet) { + void registerRecorder(Set fileExtensions, ResourceSet recorderResourceSet, UuidResolver uuidResolver) { fileExtensionToExtensionsSet.keySet().forEach( it -> checkState(!fileExtensions.contains(it), "there already is a recorder for metamodel " + it)); Set fileExtensionsSet = new HashSet<>(fileExtensions); fileExtensions.forEach(it -> fileExtensionToExtensionsSet.put(it, fileExtensionsSet)); - ChangeRecorder recorder = new ChangeRecorder(recorderResourceSet); + ChangeRecorder recorder = new ChangeRecorder(recorderResourceSet, uuidResolver); fileExtensionsToRecorder.put(fileExtensionsSet, recorder); } } @@ -106,9 +109,12 @@ private void writeModelsFile() throws IOException { private void readModelsFile() throws IOException { try { - for (String modelPath : Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath())) { - URI uri = URI.createURI(modelPath); + List modelUris = Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath()).stream().map(URI::createURI).collect(Collectors.toList()); + for (URI uri : modelUris) { loadOrCreateResource(modelsResourceSet, uri); + } + uuidResolver.loadFromUri(fileSystemLayout.getUuidResolverURI()); + for (URI uri : modelUris) { createOrLoadModel(uri); } } catch (NoSuchFileException e) { @@ -125,6 +131,11 @@ public EditableCorrespondenceModelView getCorrespondenceModel() public ModelInstance getModel(URI modelUri) { return modelInstances.get(modelUri); } + + @Override + public UuidResolver getUuidResolver() { + return uuidResolver; + } private ModelInstance getCreateOrLoadModelUnlessLoading(URI modelUri) { if (isLoading) { @@ -159,7 +170,7 @@ private void registerRecorder(ModelInstance modelInstance) { if (modelInstance.getURI().isFile() || modelInstance.getURI().isPlatform()) { String fileExtension = modelInstance.getURI().fileExtension(); if (!fileExtensionRecorderMapping.hasRecorder(fileExtension)) { - fileExtensionRecorderMapping.registerRecorder(Set.of(fileExtension), modelsResourceSet); + fileExtensionRecorderMapping.registerRecorder(Set.of(fileExtension), modelsResourceSet, uuidResolver); } ChangeRecorder recorder = fileExtensionRecorderMapping.getRecorder(fileExtension); recorder.addToRecording(modelInstance.getResource()); @@ -192,6 +203,7 @@ public void saveOrDeleteModels() { correspondenceModel.save(); try { writeModelsFile(); + uuidResolver.storeAtUri(fileSystemLayout.getUuidResolverURI()); } catch (IOException e) { throw new IllegalStateException(e); } @@ -212,7 +224,7 @@ public Iterable recordChanges(Runnable changeApplicator) { @Override public VitruviusChange applyChange(VitruviusChange change) { - return change.resolveAndApply(modelsResourceSet); + return change.resolveAndApply(uuidResolver); } @Override diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java index 372177ca91..e0154c8c04 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java @@ -12,6 +12,7 @@ import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; +import tools.vitruv.change.atomic.uuid.UuidResolver; import tools.vitruv.change.composite.description.PropagatedChange; import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.change.composite.propagation.ChangePropagationListener; @@ -63,6 +64,11 @@ public synchronized EditableCorrespondenceModelView getCorrespon public synchronized ModelInstance getModelInstance(URI modelUri) { return resourceRepository.getModel(modelUri); } + + @Override + public UuidResolver getUuidResolver() { + return resourceRepository.getUuidResolver(); + } private synchronized void save() { resourceRepository.saveOrDeleteModels(); diff --git a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend index b97e98f9d4..8ac7227229 100644 --- a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend +++ b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend @@ -21,6 +21,7 @@ import allElementTypes.AllElementTypesPackage import tools.vitruv.change.composite.MetamodelDescriptor import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView import tools.vitruv.change.correspondence.Correspondence +import tools.vitruv.change.atomic.uuid.UuidResolver /** * Utility methods for the VSUM and view test cases. @@ -31,8 +32,8 @@ class VirtualModelTestUtil { /** * Create a recorder, start recording a resource set, apply changes, stop and return the recorded changes. */ - def static VitruviusChange recordChanges(ResourceSet resourceSet, Runnable changesToPerform) { - val recorder = new ChangeRecorder(resourceSet) + def static VitruviusChange recordChanges(ResourceSet resourceSet, UuidResolver uuidResolver, Runnable changesToPerform) { + val recorder = new ChangeRecorder(resourceSet, uuidResolver) recorder.addToRecording(resourceSet) recorder.beginRecording changesToPerform.run() From c9d4f81ca35fb4848e60dc1a0b30920f527d05ea Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Wed, 9 Nov 2022 11:41:16 +0100 Subject: [PATCH 02/19] adapt to interface changes copy UUIDs to identity view --- .../DefaultStateBasedChangeResolutionStrategy.xtend | 2 ++ .../framework/views/impl/IdentityMappingViewType.xtend | 5 +++-- .../framework/vsum/internal/ResourceRepositoryImpl.java | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend index 1be044293f..7becdc5e9f 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend @@ -53,6 +53,7 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol override getChangeSequenceBetween(Resource newState, Resource oldState, UuidResolver uuidResolver) { 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() @@ -82,6 +83,7 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol override getChangeSequenceForDeleted(Resource oldState, UuidResolver uuidResolver) { 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() diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend index b7ea775b06..a9888eea57 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend @@ -37,13 +37,14 @@ class IdentityMappingViewType extends AbstractViewType Date: Wed, 9 Nov 2022 11:41:57 +0100 Subject: [PATCH 03/19] adapt tests to uuid resolver --- .../BasicStateChangePropagationTest.xtend | 85 ++++++++++++------- .../EdgeCaseStateChangeTest.xtend | 16 +++- .../StateChangePropagationTest.xtend | 14 ++- .../framework/views/impl/BasicViewTest.java | 2 +- .../views/impl/ChangeDerivingViewTest.java | 2 +- .../views/impl/ChangeRecordingViewTest.java | 2 +- .../framework/vsum/VirtualModelTest.xtend | 40 ++++++--- 7 files changed, 109 insertions(+), 52 deletions(-) diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend index 6f6be42dc7..26a1040669 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend @@ -22,6 +22,7 @@ import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories import static extension tools.vitruv.testutils.Capture.operator_doubleGreaterThan +import tools.vitruv.change.atomic.uuid.UuidResolver class BasicStateChangePropagationTest extends StateChangePropagationTest { private def getTestUri() { @@ -46,7 +47,8 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { // Create empty resource to apply generated changes to val validationResourceSet = new ResourceSetImpl() - changes.unresolve().resolveAndApply(validationResourceSet) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + changes.unresolve().resolveAndApply(validationUuidResolver) modelResource.save(null) assertEquals(1, validationResourceSet.resources.size) @@ -66,15 +68,18 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] >> modelResource ] (-modelResource).save(null) - val changes = strategyToTest.getChangeSequenceForDeleted(-modelResource) + + val validationResourceSet = new ResourceSetImpl() + val oldResource = validationResourceSet.getResource(testUri, true) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + uuidResolver.resolveResource(-modelResource, oldResource, validationUuidResolver) + + val changes = strategyToTest.getChangeSequenceForDeleted(-modelResource, uuidResolver) assertEquals(2, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) assertEquals(1, changes.EChanges.filter(DeleteEObject).size) - // Load resource to apply generated changes to - val validationResourceSet = new ResourceSetImpl() - validationResourceSet.getResource(testUri, true) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.unresolve().resolveAndApply(validationUuidResolver) assertEquals(1, validationResourceSet.resources.size) assertTrue(validationResourceSet.resources.get(0).contents.empty) @@ -94,6 +99,11 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] (-modelResource).save(null) + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) + (-modelResource).record [ contents.clear() contents += aet.Root => [ @@ -101,11 +111,8 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] ] - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - - changes.unresolve().resolveAndApply(validationResourceSet) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) + changes.unresolve().resolveAndApply(validationUuidResolver) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -126,17 +133,20 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] (-modelResource).save(null) + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) + resourceSet.record [ root.singleValuedEAttribute = 2 ] - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) assertEquals(1, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.unresolve().resolveAndApply(validationUuidResolver) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -157,13 +167,16 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] (-modelResource).save(null) + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) + resourceSet.record [ root.id = "Root2" ] - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) switch (strategyToTest.useIdentifiers) { case ONLY, case WHEN_AVAILABLE: { @@ -183,7 +196,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { } } - changes.unresolve().resolveAndApply(validationResourceSet) + changes.unresolve().resolveAndApply(validationUuidResolver) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -209,17 +222,20 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] (-modelResource).save(null) + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) + resourceSet.record [ containedRoot.singleValuedEAttribute = 1 ] - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) assertEquals(1, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.unresolve().resolveAndApply(validationUuidResolver) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -245,13 +261,16 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] (-modelResource).save(null) + val validationResourceSet = new ResourceSetImpl().withGlobalFactories() + val oldState = validationResourceSet.getResource(testUri, true) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) + resourceSet.record [ containedRoot.id = "ContainedRoot2" ] - val validationResourceSet = new ResourceSetImpl().withGlobalFactories() - val oldState = validationResourceSet.getResource(testUri, true) - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) switch (strategyToTest.useIdentifiers) { case ONLY, case WHEN_AVAILABLE: { @@ -271,7 +290,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { } } - changes.unresolve().resolveAndApply(validationResourceSet) + changes.unresolve().resolveAndApply(validationUuidResolver) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -294,6 +313,8 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationResourceSet = new ResourceSetImpl().withGlobalFactories() val oldState = validationResourceSet.getResource(testUri, true) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) val movedResourceUri = getModelURI("moved.allElementTypes") resourceSet.record [ @@ -302,12 +323,12 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] >> modelResource ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) assertEquals(2, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.unresolve().resolveAndApply(validationUuidResolver) (-modelResource).save(null) assertEquals(2, validationResourceSet.resources.size) @@ -331,6 +352,8 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationResourceSet = new ResourceSetImpl().withGlobalFactories() val oldState = validationResourceSet.getResource(testUri, true) + val validationUuidResolver = UuidResolver.create(validationResourceSet) + uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) val movedResourceUri = getModelURI("moved.allElementTypes") resourceSet.record [ @@ -341,13 +364,13 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] >> modelResource ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) + val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) assertEquals(3, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.unresolve().resolveAndApply(validationUuidResolver) (-modelResource).save(null) assertEquals(2, validationResourceSet.resources.size) diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend index f3a16d8788..9d1fed3f14 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend @@ -5,6 +5,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import static org.junit.jupiter.api.Assertions.assertThrows +import tools.vitruv.change.atomic.uuid.UuidResolver class EdgeCaseStateChangeTest extends StateChangePropagationTest { @@ -33,7 +34,20 @@ class EdgeCaseStateChangeTest extends StateChangePropagationTest { @MethodSource("strategiesToTest") def void testNullResources(StateBasedChangeResolutionStrategy strategyToTest) { val Resource nullResource = null - assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceBetween(nullResource, nullResource)] + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForCreated(nullResource)] + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceBetween(nullResource, nullResource, uuidResolver)] + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForDeleted(nullResource, uuidResolver)] + } + + /** + * Tests invalid input: null instead of uuid resolver. + */ + @ParameterizedTest() + @MethodSource("strategiesToTest") + def void testNullUuidResolver(StateBasedChangeResolutionStrategy strategyToTest) { + val UuidResolver nullUuidResolver = null + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceBetween(umlModel, umlCheckpoint, nullUuidResolver)] + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForDeleted(umlModel, nullUuidResolver)] } } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend index a375fd4da0..fa95a5cd74 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend @@ -29,6 +29,7 @@ import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories +import tools.vitruv.change.atomic.uuid.UuidResolver @ExtendWith(TestProjectManager, TestLogging, RegisterMetamodelsInStandalone) abstract class StateChangePropagationTest { @@ -50,7 +51,10 @@ abstract class StateChangePropagationTest { var ChangeRecorder changeRecorder @Accessors(PROTECTED_GETTER) var ResourceSet resourceSet + @Accessors(PROTECTED_GETTER) + var UuidResolver uuidResolver var ResourceSet checkpointResourceSet + var UuidResolver checkpointUuidResolver /** * Creates the strategy, sets up the test model and prepares everything for determining changes. @@ -60,8 +64,10 @@ abstract class StateChangePropagationTest { this.testProjectFolder = testProjectFolder // Setup: resourceSet = new ResourceSetImpl().withGlobalFactories() + uuidResolver = UuidResolver.create(resourceSet) checkpointResourceSet = new ResourceSetImpl().withGlobalFactories() - changeRecorder = new ChangeRecorder(resourceSet) + checkpointUuidResolver = UuidResolver.create(checkpointResourceSet) + changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) // Create mockup models: resourceSet.record [ createPcmMockupModel() @@ -97,7 +103,7 @@ abstract class StateChangePropagationTest { protected def compareChanges(Resource model, Resource checkpoint, StateBasedChangeResolutionStrategy strategyToTest) { model.save(null) val deltaBasedChange = resourceSet.endRecording - val stateBasedChange = strategyToTest.getChangeSequenceBetween(model, checkpoint) + val stateBasedChange = strategyToTest.getChangeSequenceBetween(model, checkpoint, checkpointUuidResolver) assertNotNull(stateBasedChange) val message = getTextualRepresentation(stateBasedChange, deltaBasedChange) val stateBasedChangedObjects = stateBasedChange.affectedAndReferencedEObjects @@ -163,7 +169,9 @@ abstract class StateChangePropagationTest { } private def Resource createCheckpoint(Resource original) { - return checkpointResourceSet.getResource(original.URI, true) + val resource = checkpointResourceSet.getResource(original.URI, true) + uuidResolver.resolveResource(original, resource, checkpointUuidResolver) + return resource } protected def URI getModelURI(String modelFileName) { diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java index cda335aa31..e1ca931372 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java @@ -143,7 +143,7 @@ public void withoutPreviousModification() throws Exception { @DisplayName("with previous modification") public void withPreviousModification() throws Exception { try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); + view.modifyContents((resourceSet, uuidResolver) -> resourceSet.createResource(URI.createURI("test://test.aet"))); assertThrows(IllegalStateException.class, () -> view.update()); } } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java index 93e0e38735..a827189aa2 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java @@ -152,7 +152,7 @@ public void withoutPreviousModification() throws Exception { public void withPreviousModification() throws Exception { try (ChangeDerivingView view = new ChangeDerivingView(new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection), new DefaultStateBasedChangeResolutionStrategy())) { - view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); + view.modifyContents((resourceSet, uuidResolver) -> resourceSet.createResource(URI.createURI("test://test.aet"))); assertThrows(IllegalStateException.class, () -> view.update()); } } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java index f76fa6e4c5..cb6ed3486f 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java @@ -149,7 +149,7 @@ public void withoutPreviousModification() throws Exception { @DisplayName("with previous modification") public void withPreviousModification() throws Exception { try (ChangeRecordingView view = new ChangeRecordingView(new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); + view.modifyContents((resourceSet, uuidResolver) -> resourceSet.createResource(URI.createURI("test://test.aet"))); assertThrows(IllegalStateException.class, () -> view.update()); } } diff --git a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend index 4fd10d6894..f4c1751150 100644 --- a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend +++ b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend @@ -33,6 +33,7 @@ import static extension edu.kit.ipd.sdq.commons.util.java.lang.IterableUtil.clai import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceUtil.getFirstRootEObject import static extension tools.vitruv.framework.vsum.VirtualModelTestUtil.* +import tools.vitruv.change.atomic.uuid.UuidResolver @ExtendWith(TestProjectManager) class VirtualModelTest { @@ -51,7 +52,8 @@ class VirtualModelTest { def void propagateIntoVirtualModel() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - val changeRecorder = new ChangeRecorder(resourceSet) + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ @@ -70,7 +72,8 @@ class VirtualModelTest { def void propagateIntoVirtualModelWithConsistency() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - val changeRecorder = new ChangeRecorder(resourceSet) + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ @@ -93,7 +96,8 @@ class VirtualModelTest { def void singleChangeForRootElementInMultipleResource() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - val changeRecorder = new ChangeRecorder(resourceSet) + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val containedRoot = aet.Root @@ -123,7 +127,8 @@ class VirtualModelTest { def void singleChangeForElementContainedInRootElementInMultipleResource() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - val changeRecorder = new ChangeRecorder(resourceSet) + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val containedRoot = aet.Root @@ -160,7 +165,8 @@ class VirtualModelTest { def void savedVirtualModel() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - val changeRecorder = new ChangeRecorder(resourceSet) + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ @@ -180,7 +186,8 @@ class VirtualModelTest { def void reloadVirtualModel() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - val changeRecorder = new ChangeRecorder(resourceSet) + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val root = aet.Root @@ -209,7 +216,8 @@ class VirtualModelTest { def void reloadVirtualModelWithConsistency() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - val changeRecorder = new ChangeRecorder(resourceSet) + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val root = aet.Root @@ -238,7 +246,8 @@ class VirtualModelTest { def void moveCorrespondingToOtherResourceAndBack() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - val changeRecorder = new ChangeRecorder(resourceSet) + val uuidResolver = UuidResolver.create(resourceSet) + val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val root = aet.Root @@ -275,7 +284,8 @@ class VirtualModelTest { def void createView() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - virtualModel.createAndPropagateRoot(resourceSet, ROOT_ID) + val uuidResolver = UuidResolver.create(resourceSet) + virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) val testView = virtualModel.createTestView // Check initial state: assertThat(new HashSet(testView.rootObjects), not(is(emptySet()))) @@ -290,11 +300,12 @@ class VirtualModelTest { def void updateView() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - virtualModel.createAndPropagateRoot(resourceSet, ROOT_ID) + val uuidResolver = UuidResolver.create(resourceSet) + virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) val testView = virtualModel.createTestView // Modify model - virtualModel.propagateChange(resourceSet.recordChanges [ + virtualModel.propagateChange(resourceSet.recordChanges(uuidResolver) [ val resource = resourceSet.resources.claimOne resource.firstRootEObject as Root => [ multiValuedContainmentEReference += aet.NonRoot => [ @@ -321,7 +332,8 @@ class VirtualModelTest { def void commitView() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories - virtualModel.createAndPropagateRoot(resourceSet, ROOT_ID) + val uuidResolver = UuidResolver.create(resourceSet) + virtualModel.createAndPropagateRoot(resourceSet, uuidResolver, ROOT_ID) val testView = virtualModel.createTestView.withChangeRecordingTrait // Modify view: @@ -358,8 +370,8 @@ class VirtualModelTest { projectFolder.resolve("vsum") } - def private createAndPropagateRoot(VirtualModel virtualModel, ResourceSet resourceSet, String rootId) { - virtualModel.propagateChange(resourceSet.recordChanges [ + def private createAndPropagateRoot(VirtualModel virtualModel, ResourceSet resourceSet, UuidResolver uuidResolver, String rootId) { + virtualModel.propagateChange(resourceSet.recordChanges(uuidResolver) [ resourceSet.createResource(projectFolder.createTestModelResourceUri("")) => [ contents += aet.Root => [ id = rootId From 03f1676dea2d6e79965d85b514ef8959aae4079e Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Wed, 9 Nov 2022 11:46:25 +0100 Subject: [PATCH 04/19] added todo not regarding visibility of dependency package in legacy view tests --- .../vitruv/testutils/LegacyVitruvApplicationTest.xtend | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend index 1aac450c7c..b3f852a3d5 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend +++ b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend @@ -24,7 +24,8 @@ abstract class LegacyVitruvApplicationTest extends VitruvApplicationTest impleme NonTransactionalTestView testView override generateTestView(Path testProjectPath, Path vsumPath) { - val testView = new DefaultVirtualModelBasedTestView(testProjectPath, vsumPath, changePropagationSpecifications, uriMode) + val testView = new DefaultVirtualModelBasedTestView(testProjectPath, vsumPath, changePropagationSpecifications, + uriMode) testView.disposeViewResourcesAfterPropagation = false this.testView = testView return testView @@ -39,7 +40,7 @@ abstract class LegacyVitruvApplicationTest extends VitruvApplicationTest impleme return internalVirtualModel.correspondenceModel.getCorrespondingEObjects(resolvedObject, tag).filter(type) } } - + private def getInternalVirtualModel() { return virtualModel as InternalVirtualModel } @@ -49,8 +50,10 @@ abstract class LegacyVitruvApplicationTest extends VitruvApplicationTest impleme } private def dispatch EObject resolveInVirtualModel(EObject object) { + // TODO: JW as soon as this class becomes not needed anymore, we should hide the tools.vitruv.change.atomic.id package in tools.vitruv.change.atomic (of Vitruv-Change repository). if (object.eResource !== null) { - internalVirtualModel.getModelInstance(object.eResource.URI).resource.getEObject(object.hierarchicUriFragment) + internalVirtualModel.getModelInstance(object.eResource.URI).resource.getEObject( + object.hierarchicUriFragment) } } From 8283df223a9f52e1c90527ee15ca71dc7738076b Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Wed, 9 Nov 2022 15:22:13 +0100 Subject: [PATCH 05/19] code simplification --- .../vsum/internal/ResourceRepositoryImpl.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java index b8353f9f1a..041549be1b 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java @@ -108,18 +108,17 @@ private void writeModelsFile() throws IOException { } private void readModelsFile() throws IOException { + List modelUris; try { - List modelUris = Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath()).stream().map(URI::createURI).collect(Collectors.toList()); - for (URI uri : modelUris) { - loadOrCreateResource(modelsResourceSet, uri); - } - uuidResolver.loadFromUri(fileSystemLayout.getUuidsURI()); - for (URI uri : modelUris) { - createOrLoadModel(uri); - } + modelUris = Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath()).stream().map(URI::createURI).collect(Collectors.toList()); + } catch (NoSuchFileException e) { // There are no existing models, so don't do anything + return; } + modelUris.forEach(uri -> loadOrCreateResource(modelsResourceSet, uri)); + uuidResolver.loadFromUri(fileSystemLayout.getUuidsURI()); + modelUris.forEach(this::createOrLoadModel); } @Override From 8db9b23a992374ba03ef4a3d045facf0403c2b9b Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Thu, 10 Nov 2022 11:29:50 +0100 Subject: [PATCH 06/19] remove registered UUIDs for unloaded resources when updating a view --- .../framework/views/impl/IdentityMappingViewType.xtend | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend index a9888eea57..3e803b7e49 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend @@ -40,10 +40,14 @@ class IdentityMappingViewType extends AbstractViewType Date: Mon, 14 Nov 2022 14:57:52 +0100 Subject: [PATCH 07/19] rewrite identity mapping view type in Java adapt view tests to enforce non-null UUID resolver --- .../views/impl/IdentityMappingViewType.java | 63 +++++++++++++++++++ .../views/impl/IdentityMappingViewType.xtend | 54 ---------------- .../impl/IdentityMappingViewTypeTest.java | 8 +++ 3 files changed, 71 insertions(+), 54 deletions(-) create mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java delete mode 100644 bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java new file mode 100644 index 0000000000..77aa89515e --- /dev/null +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java @@ -0,0 +1,63 @@ +package tools.vitruv.framework.views.impl; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.emf.ecore.resource.Resource; + +import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.ViewSelection; +import tools.vitruv.framework.views.selectors.DirectViewElementSelector; +import tools.vitruv.framework.views.util.ResourceCopier; + +/** + * A view type that allows creating views based on a basic element-wise selection mechanism + * and providing a one-to-one (identity) mapping of elements within the {@link ViewSource} + * to a created {@link View}. + */ +public class IdentityMappingViewType extends AbstractViewType { + public IdentityMappingViewType(String name) { + super(name); + } + + @Override + public DirectViewElementSelector createSelector(ChangeableViewSource viewSource) { + return new DirectViewElementSelector(this, viewSource, + viewSource.getViewSourceModels().stream().map(resource -> { + if (!resource.getContents().isEmpty() && ResourceCopier.requiresFullCopy(resource)) { + // We can only copy writable UML resources as a whole, so no option to select + // specific root elements + return List.of(resource.getContents().get(0)); + } + return resource.getContents(); + }).flatMap(List::stream).filter(it -> it != null).collect(Collectors.toList())); + } + + @Override + public ModifiableView createView(DirectViewElementSelector selector) { + checkArgument(selector.getViewType() == this, "cannot create view with selector for different view type"); + return new BasicView(selector.getViewType(), selector.getViewSource(), selector.getSelection()); + } + + @Override + public void updateView(ModifiableView view) { + view.modifyContents((viewResourceSet, viewUuidResolver) -> { + viewResourceSet.getResources().forEach(Resource::unload); + viewResourceSet.getResources().clear(); + viewUuidResolver.endTransaction(); + + Collection viewSources = view.getViewSource().getViewSourceModels(); + ViewSelection selection = view.getSelection(); + List resourcesWithSelectedElements = viewSources.stream() + .filter(resource -> resource.getContents().stream().anyMatch(selection::isViewObjectSelected)) + .collect(Collectors.toList()); + Map mapping = ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, + viewResourceSet, selection::isViewObjectSelected); + view.getViewSource().getUuidResolver().resolveResources(mapping, viewUuidResolver); + }); + } +} diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend deleted file mode 100644 index 3e803b7e49..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend +++ /dev/null @@ -1,54 +0,0 @@ -package tools.vitruv.framework.views.impl - -import tools.vitruv.framework.views.ChangeableViewSource -import tools.vitruv.framework.views.View -import tools.vitruv.framework.views.ViewSource -import tools.vitruv.framework.views.selectors.DirectViewElementSelector -import tools.vitruv.framework.views.util.ResourceCopier - -import static com.google.common.base.Preconditions.checkArgument - -import static extension tools.vitruv.framework.views.util.ResourceCopier.requiresFullCopy - -/** - * A view type that allows creating views based on a basic element-wise selection mechanism - * and providing a one-to-one (identity) mapping of elements within the {@link ViewSource} - * to a created {@link View}. - */ -class IdentityMappingViewType extends AbstractViewType { - - new(String name) { - super(name) - } - - override createSelector(ChangeableViewSource viewSource) { - return new DirectViewElementSelector(this, viewSource, viewSource.viewSourceModels.flatMap [ - if (requiresFullCopy) { - #[contents.head] // We can only copy writable UML resources as a whole, so no option to select specific root elements - } else { - contents - } - ].filterNull.toList) - } - - override createView(DirectViewElementSelector selector) { - checkArgument(selector.viewType === this, "cannot create view with selector for different view type") - return new BasicView(selector.viewType, selector.viewSource, selector.selection) - } - - override updateView(ModifiableView view) { - view.modifyContents [ viewResourceSet, uuidResolver | - viewResourceSet.resources.forEach[unload] - viewResourceSet.resources.clear - uuidResolver.endTransaction - - val viewSources = view.viewSource.viewSourceModels - val selection = view.selection - val resourcesWithSelectedElements = viewSources.filter[contents.exists[selection.isViewObjectSelected(it)]] - val mapping = ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, viewResourceSet) [ - selection.isViewObjectSelected(it) - ] - view.viewSource.uuidResolver?.resolveResources(mapping, uuidResolver) - ] - } -} diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java index 38ddba8fa3..042c1e1403 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java @@ -30,6 +30,7 @@ import com.google.common.collect.FluentIterable; import allElementTypes.Root; +import tools.vitruv.change.atomic.uuid.UuidResolver; import tools.vitruv.framework.views.ChangeableViewSource; import tools.vitruv.framework.views.View; import tools.vitruv.framework.views.ViewType; @@ -124,6 +125,7 @@ public void initializeViewTypeAndResourceSet() { @DisplayName("with empty source") public void withNoElements() throws Exception { ChangeableViewSource viewSource = mock(ChangeableViewSource.class); + when(viewSource.getUuidResolver()).thenReturn(UuidResolver.create(testResourceSet)); DirectViewElementSelector selector = basicViewType.createSelector(viewSource); try (View view = basicViewType.createView(selector)) { assertThat(view.getRootObjects(), not(hasItem(anything()))); @@ -147,6 +149,7 @@ public void withNoSelectedElement() throws Exception { resource.getContents().add(rootElement); ChangeableViewSource viewSource = mock(ChangeableViewSource.class); when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); + when(viewSource.getUuidResolver()).thenReturn(UuidResolver.create(testResourceSet)); DirectViewElementSelector selector = basicViewType.createSelector(viewSource); try (View view = basicViewType.createView(selector)) { assertThat(view.getRootObjects(), not(hasItem(anything()))); @@ -162,6 +165,7 @@ public void withSingleSelectedElement() throws Exception { resource.getContents().add(rootElement); ChangeableViewSource viewSource = mock(ChangeableViewSource.class); when(viewSource.getViewSourceModels()).thenReturn(Set.of(resource)); + when(viewSource.getUuidResolver()).thenReturn(UuidResolver.create(testResourceSet)); DirectViewElementSelector selector = basicViewType.createSelector(viewSource); selector.setSelected(rootElement, true); try (View view = basicViewType.createView(selector)) { @@ -183,6 +187,7 @@ public void withOneOfTwoElementsSelected() throws Exception { secondResource.getContents().add(secondRootElement); ChangeableViewSource viewSource = mock(ChangeableViewSource.class); when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); + when(viewSource.getUuidResolver()).thenReturn(UuidResolver.create(testResourceSet)); DirectViewElementSelector selector = basicViewType.createSelector(viewSource); selector.setSelected(firstRootElement, true); try (View view = basicViewType.createView(selector)) { @@ -204,6 +209,7 @@ public void withBothOfTwoElementsSelected() throws Exception { secondResource.getContents().add(secondRootElement); ChangeableViewSource viewSource = mock(ChangeableViewSource.class); when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); + when(viewSource.getUuidResolver()).thenReturn(UuidResolver.create(testResourceSet)); DirectViewElementSelector selector = basicViewType.createSelector(viewSource); selector.setSelected(firstRootElement, true); selector.setSelected(secondRootElement, true); @@ -237,6 +243,7 @@ public void forTwoResourcesWithContainmentInBetween() throws Exception { secondResource.getContents().add(secondRootElement); ChangeableViewSource viewSource = mock(ChangeableViewSource.class); when(viewSource.getViewSourceModels()).thenReturn(Set.of(firstResource, secondResource)); + when(viewSource.getUuidResolver()).thenReturn(UuidResolver.create(testResourceSet)); DirectViewElementSelector selector = basicViewType.createSelector(viewSource); selector.setSelected(firstRootElement, true); selector.setSelected(secondRootElement, true); @@ -267,6 +274,7 @@ public void initializeViewTypeAndResourceSetAndViewSource() { this.testResourceSet = withGlobalFactories(new ResourceSetImpl()); this.viewSource = mock(ChangeableViewSource.class); when(viewSource.getViewSourceModels()).thenReturn(testResourceSet.getResources()); + when(viewSource.getUuidResolver()).thenReturn(UuidResolver.create(testResourceSet)); } private Root createResourceWithSingleRoot(URI uri) { From 9869aac15b7f4657dae89dcadda6b91e0b32f076 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Mon, 14 Nov 2022 15:22:58 +0100 Subject: [PATCH 08/19] migrate `ResourceRepositoryImpl` to use only one change recorder as otherwise transactions on the shared UUID resolver are ended before all IDs are assigned --- .../vsum/internal/ResourceRepositoryImpl.java | 73 +++++-------------- 1 file changed, 17 insertions(+), 56 deletions(-) diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java index 041549be1b..480b74f7a8 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java @@ -11,12 +11,10 @@ import java.nio.file.NoSuchFileException; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import org.apache.log4j.Logger; import org.eclipse.emf.common.util.URI; @@ -41,48 +39,15 @@ class ResourceRepositoryImpl implements ModelRepository { private final ResourceSet modelsResourceSet = withGlobalFactories(new ResourceSetImpl()); private final Map modelInstances = new HashMap<>(); - private final FileExtensionRecorderMapping fileExtensionRecorderMapping = new FileExtensionRecorderMapping(); private final PersistableCorrespondenceModel correspondenceModel; private final UuidResolver uuidResolver = UuidResolver.create(modelsResourceSet); + private final ChangeRecorder changeRecorder = new ChangeRecorder(modelsResourceSet, uuidResolver); private final VsumFileSystemLayout fileSystemLayout; private boolean isRecording = false; private boolean isLoading = false; - /** - * Manages change recorders for file extensions. Ensures that only one change - * recorder per file extension exists. A recorder is assigned to a set of file - * extensions (for the case that multiple file extensions belong to the same - * domain of models and should be recorder together) and recorders can be - * retrieved for a given file extension. - */ - private static class FileExtensionRecorderMapping { - private final Map, ChangeRecorder> fileExtensionsToRecorder = new HashMap<>(); - private final Map> fileExtensionToExtensionsSet = new HashMap<>(); - - Set getRecorders() { - return new HashSet<>(fileExtensionsToRecorder.values()); - } - - boolean hasRecorder(String fileExtension) { - return fileExtensionsToRecorder.containsKey(fileExtensionToExtensionsSet.get(fileExtension)); - } - - ChangeRecorder getRecorder(String fileExtension) { - return fileExtensionsToRecorder.get(fileExtensionToExtensionsSet.get(fileExtension)); - } - - void registerRecorder(Set fileExtensions, ResourceSet recorderResourceSet, UuidResolver uuidResolver) { - fileExtensionToExtensionsSet.keySet().forEach( - it -> checkState(!fileExtensions.contains(it), "there already is a recorder for metamodel " + it)); - Set fileExtensionsSet = new HashSet<>(fileExtensions); - fileExtensions.forEach(it -> fileExtensionToExtensionsSet.put(it, fileExtensionsSet)); - ChangeRecorder recorder = new ChangeRecorder(recorderResourceSet, uuidResolver); - fileExtensionsToRecorder.put(fileExtensionsSet, recorder); - } - } - ResourceRepositoryImpl(VsumFileSystemLayout fileSystemLayout) { this.fileSystemLayout = fileSystemLayout; this.correspondenceModel = createPersistableCorrespondenceModel(fileSystemLayout.getCorrespondencesURI()); @@ -103,15 +68,16 @@ public void loadExistingModels() { } private void writeModelsFile() throws IOException { - Files.write(fileSystemLayout.getModelsNamesFilesPath(), - modelsResourceSet.getResources().stream().map(Resource::getURI).map(URI::toString).toList()); + Files.write(fileSystemLayout.getModelsNamesFilesPath(), modelsResourceSet.getResources().stream() + .map(Resource::getURI).map(URI::toString).toList()); } private void readModelsFile() throws IOException { List modelUris; try { - modelUris = Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath()).stream().map(URI::createURI).collect(Collectors.toList()); - + modelUris = Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath()).stream().map(URI::createURI) + .toList(); + } catch (NoSuchFileException e) { // There are no existing models, so don't do anything return; @@ -130,7 +96,7 @@ public EditableCorrespondenceModelView getCorrespondenceModel() public ModelInstance getModel(URI modelUri) { return modelInstances.get(modelUri); } - + @Override public UuidResolver getUuidResolver() { return uuidResolver; @@ -167,14 +133,9 @@ private ModelInstance createOrLoadModel(URI modelUri) { private void registerRecorder(ModelInstance modelInstance) { // Only monitor modifiable models (file / platform URIs, not pathmap URIs) if (modelInstance.getURI().isFile() || modelInstance.getURI().isPlatform()) { - String fileExtension = modelInstance.getURI().fileExtension(); - if (!fileExtensionRecorderMapping.hasRecorder(fileExtension)) { - fileExtensionRecorderMapping.registerRecorder(Set.of(fileExtension), modelsResourceSet, uuidResolver); - } - ChangeRecorder recorder = fileExtensionRecorderMapping.getRecorder(fileExtension); - recorder.addToRecording(modelInstance.getResource()); - if (isRecording && !recorder.isRecording()) { - recorder.beginRecording(); + changeRecorder.addToRecording(modelInstance.getResource()); + if (isRecording && !changeRecorder.isRecording()) { + changeRecorder.beginRecording(); } } } @@ -210,27 +171,27 @@ public void saveOrDeleteModels() { @Override public Iterable recordChanges(Runnable changeApplicator) { - fileExtensionRecorderMapping.getRecorders().forEach(ChangeRecorder::beginRecording); + changeRecorder.beginRecording(); isRecording = true; LOGGER.debug("Start recording virtual model"); changeApplicator.run(); LOGGER.debug("End recording virtual model"); isRecording = false; - fileExtensionRecorderMapping.getRecorders().forEach(ChangeRecorder::endRecording); - return fileExtensionRecorderMapping.getRecorders().stream().map(ChangeRecorder::getChange) - .filter(TransactionalChange::containsConcreteChange).toList(); + changeRecorder.endRecording(); + TransactionalChange change = changeRecorder.getChange(); + return change.containsConcreteChange() ? List.of(change) : List.of(); } @Override public VitruviusChange applyChange(VitruviusChange change) { return change.resolveAndApply(uuidResolver); } - + @Override public URI getMetadataModelURI(String... metadataKey) { return fileSystemLayout.getConsistencyMetadataModelURI(metadataKey); } - + @Override public Resource getModelResource(URI uri) { return getCreateOrLoadModel(uri).getResource(); @@ -243,7 +204,7 @@ public Collection getModelResources() { @Override public void close() { - fileExtensionRecorderMapping.fileExtensionsToRecorder.values().forEach(ChangeRecorder::close); + changeRecorder.close(); modelsResourceSet.getResources().forEach(Resource::unload); modelsResourceSet.getResources().clear(); } From c7dc52a4e20a94c0a7b52c2956ea55f30e9ace87 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Tue, 15 Nov 2022 09:57:53 +0100 Subject: [PATCH 09/19] adapt to changed test view API --- .../framework/vsum/internal/ModelRepository.xtend | 8 +------- .../testutils/DefaultVirtualModelBasedTestView.xtend | 11 ++++++----- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend index de4db8de88..dc664e44c2 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ModelRepository.xtend @@ -1,10 +1,9 @@ package tools.vitruv.framework.vsum.internal -import org.eclipse.emf.common.util.URI import java.util.Collection +import org.eclipse.emf.common.util.URI import org.eclipse.emf.ecore.resource.Resource import tools.vitruv.change.propagation.ChangeRecordingModelRepository -import tools.vitruv.change.atomic.uuid.UuidResolver package interface ModelRepository extends ChangeRecordingModelRepository { /** @@ -18,9 +17,4 @@ package interface ModelRepository extends ChangeRecordingModelRepository { def void saveOrDeleteModels() def Collection getModelResources() - - /** - * Returns the {@link UuidResolver} associated with all resources in this repository. - */ - def UuidResolver getUuidResolver() } diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend index 4ba7c0b432..1427c79ba8 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend +++ b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend @@ -1,13 +1,13 @@ package tools.vitruv.testutils -import tools.vitruv.framework.vsum.internal.InternalVirtualModel -import org.eclipse.xtend.lib.annotations.Delegate import java.nio.file.Path -import tools.vitruv.framework.vsum.VirtualModelBuilder +import org.eclipse.xtend.lib.annotations.Delegate import tools.vitruv.change.propagation.ChangePropagationSpecification -import tools.vitruv.testutils.views.UriMode +import tools.vitruv.framework.vsum.VirtualModelBuilder +import tools.vitruv.framework.vsum.internal.InternalVirtualModel import tools.vitruv.testutils.views.ChangePublishingTestView import tools.vitruv.testutils.views.NonTransactionalTestView +import tools.vitruv.testutils.views.UriMode class DefaultVirtualModelBasedTestView implements VirtualModelBasedTestView, NonTransactionalTestView { InternalVirtualModel virtualModel @@ -38,7 +38,8 @@ class DefaultVirtualModelBasedTestView implements VirtualModelBasedTestView, Non } private def NonTransactionalTestView generateTestView(Path testProjectPath, TestUserInteraction userInteraction) { - new ChangePublishingTestView(testProjectPath, userInteraction, this.uriMode, virtualModel) + new ChangePublishingTestView(testProjectPath, userInteraction, this.uriMode, virtualModel, + virtualModel.uuidResolver)[virtualModel.getModelInstance(it).resource] } override getVirtualModel() { From 6a1fbaff525eed01140986ad289dca7a7afe600d Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Tue, 15 Nov 2022 10:51:25 +0100 Subject: [PATCH 10/19] fix virtual model based view to avoid nullpointer exception when accessing resources --- .../vitruv/framework/vsum/internal/ResourceRepositoryImpl.java | 1 - .../vitruv/testutils/DefaultVirtualModelBasedTestView.xtend | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java index 480b74f7a8..0b0f5aa2c1 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java @@ -1,6 +1,5 @@ package tools.vitruv.framework.vsum.internal; -import static com.google.common.base.Preconditions.checkState; import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.getOrCreateResource; import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.loadOrCreateResource; import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend index 1427c79ba8..21b0c31a11 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend +++ b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/DefaultVirtualModelBasedTestView.xtend @@ -39,7 +39,7 @@ class DefaultVirtualModelBasedTestView implements VirtualModelBasedTestView, Non private def NonTransactionalTestView generateTestView(Path testProjectPath, TestUserInteraction userInteraction) { new ChangePublishingTestView(testProjectPath, userInteraction, this.uriMode, virtualModel, - virtualModel.uuidResolver)[virtualModel.getModelInstance(it).resource] + virtualModel.uuidResolver)[virtualModel.getModelInstance(it)?.resource] } override getVirtualModel() { From e33507d066fdb42e633131a9b61d1ae50ee90fd7 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Fri, 25 Nov 2022 13:51:36 +0100 Subject: [PATCH 11/19] adapt to change recorder and id management split --- .../StateBasedChangeResolutionStrategy.xtend | 7 +-- .../views/impl/ChangeDerivingView.xtend | 6 ++- .../views/impl/ChangeRecordingView.xtend | 7 ++- .../vsum/internal/ResourceRepositoryImpl.java | 5 +- .../LegacyVitruvApplicationTest.xtend | 11 +++-- .../EdgeCaseStateChangeTest.xtend | 16 +------ .../StateChangePropagationTest.xtend | 13 +++-- .../views/impl/ChangeDerivingViewTest.java | 2 +- .../framework/vsum/VirtualModelTest.xtend | 47 +++++++++++-------- .../framework/vsum/VirtualModelTestUtil.xtend | 20 ++++---- 10 files changed, 69 insertions(+), 65 deletions(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend index 4690b4e68a..4ccd82a26c 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend @@ -2,7 +2,6 @@ package tools.vitruv.framework.views.changederivation import org.eclipse.emf.ecore.resource.Resource import tools.vitruv.change.composite.description.VitruviusChange -import tools.vitruv.change.atomic.uuid.UuidResolver /** * Strategy for resolving state-based changes to individual change sequences. @@ -14,10 +13,9 @@ interface StateBasedChangeResolutionStrategy { * Resolves the state-based delta of two resources and returns the correlating change sequences. * @param newState is the new state of the resource, must not be null and must not contain proxies. * @param oldState is the current or old state of the resource, must not be null and must not contain proxies. - * @param uuidResolver contains the UUIDs for the objects in oldState. * @return a {@link VitruviusChange} that contains the individual change sequence. */ - def VitruviusChange getChangeSequenceBetween(Resource newState, Resource oldState, UuidResolver uuidResolver) + def VitruviusChange getChangeSequenceBetween(Resource newState, Resource oldState) /** * Resolves the state-based delta for creating the given resource and returns the correlating change sequences. @@ -29,8 +27,7 @@ interface StateBasedChangeResolutionStrategy { /** * Resolves the state-based delta for deleting the given resource and returns the correlating change sequences. * @param oldState is the new state of the resource, must not be null and must not contain proxies. - * @param uuidResolver contains the UUIDs for the objects in oldState. * @return a {@link VitruviusChange} that contains the individual change sequence. */ - def VitruviusChange getChangeSequenceForDeleted(Resource oldState, UuidResolver uuidResolver) + def VitruviusChange getChangeSequenceForDeleted(Resource oldState) } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend index 4d0fc2e1ab..cad27c6ce2 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend @@ -7,6 +7,7 @@ import org.eclipse.emf.ecore.resource.Resource import org.eclipse.emf.ecore.resource.ResourceSet import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl import org.eclipse.xtend.lib.annotations.Delegate +import tools.vitruv.change.atomic.EChangeIdManager import tools.vitruv.change.composite.description.VitruviusChange import tools.vitruv.framework.views.CommittableView import tools.vitruv.framework.views.View @@ -60,6 +61,7 @@ class ChangeDerivingView implements ModifiableView, CommittableView { allResources.addAll(view.viewResourceSet.resources) // consider newly added resources for (changedResource : allResources.filter[!URI.isPathmap]) { val change = generateChange(changedResource, originalStateResourceMapping.get(changedResource)) + EChangeIdManager.setOrGenerateIds(change.EChanges, view.uuidResolver) if (change.containsConcreteChange) { propagatedChanges += viewSource.propagateChange(change) } @@ -79,9 +81,9 @@ class ChangeDerivingView implements ModifiableView, CommittableView { if (referenceState === null) { return changeResolutionStrategy.getChangeSequenceForCreated(newState) } else if (newState === null) { - return changeResolutionStrategy.getChangeSequenceForDeleted(referenceState, view.uuidResolver) + return changeResolutionStrategy.getChangeSequenceForDeleted(referenceState) } else { - return changeResolutionStrategy.getChangeSequenceBetween(newState, referenceState, view.uuidResolver) + return changeResolutionStrategy.getChangeSequenceBetween(newState, referenceState) } } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend index 158827536a..9eee4c15d2 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend @@ -8,6 +8,7 @@ import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionS import static com.google.common.base.Preconditions.checkArgument import static com.google.common.base.Preconditions.checkState +import tools.vitruv.change.atomic.EChangeIdManager /** * A {@link View} that records changes to its resources and allows to propagate them @@ -32,7 +33,7 @@ class ChangeRecordingView implements ModifiableView, CommittableView { } private def setupChangeRecorder() { - changeRecorder = new ChangeRecorder(view.viewResourceSet, view.uuidResolver) + changeRecorder = new ChangeRecorder(view.viewResourceSet) changeRecorder.addToRecording(view.viewResourceSet) changeRecorder.beginRecording() } @@ -40,7 +41,9 @@ class ChangeRecordingView implements ModifiableView, CommittableView { override commitChanges() { view.checkNotClosed() changeRecorder.endRecording() - val propagatedChanges = viewSource.propagateChange(changeRecorder.change) + val recordedChange = changeRecorder.change + EChangeIdManager.setOrGenerateIds(recordedChange.EChanges, view.uuidResolver) + val propagatedChanges = viewSource.propagateChange(recordedChange) view.viewChanged = false changeRecorder.beginRecording() return propagatedChanges diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java index 0b0f5aa2c1..770936502c 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java @@ -22,6 +22,7 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import tools.vitruv.change.atomic.EChangeIdManager; import tools.vitruv.change.atomic.uuid.UuidResolver; import tools.vitruv.change.composite.description.TransactionalChange; import tools.vitruv.change.composite.description.VitruviusChange; @@ -40,7 +41,7 @@ class ResourceRepositoryImpl implements ModelRepository { private final Map modelInstances = new HashMap<>(); private final PersistableCorrespondenceModel correspondenceModel; private final UuidResolver uuidResolver = UuidResolver.create(modelsResourceSet); - private final ChangeRecorder changeRecorder = new ChangeRecorder(modelsResourceSet, uuidResolver); + private final ChangeRecorder changeRecorder = new ChangeRecorder(modelsResourceSet); private final VsumFileSystemLayout fileSystemLayout; @@ -178,6 +179,7 @@ public Iterable recordChanges(Runnable changeApplicator) { isRecording = false; changeRecorder.endRecording(); TransactionalChange change = changeRecorder.getChange(); + EChangeIdManager.setOrGenerateIds(change.getEChanges(), uuidResolver); return change.containsConcreteChange() ? List.of(change) : List.of(); } @@ -206,5 +208,6 @@ public void close() { changeRecorder.close(); modelsResourceSet.getResources().forEach(Resource::unload); modelsResourceSet.getResources().clear(); + uuidResolver.endTransaction(); } } diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend index b3f852a3d5..b1c57bcb3e 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend +++ b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend @@ -1,16 +1,17 @@ package tools.vitruv.testutils -import tools.vitruv.testutils.VitruvApplicationTest -import java.nio.file.Path import edu.kit.ipd.sdq.activextendannotations.DelegateExcept -import org.eclipse.emf.ecore.EObject +import java.nio.file.Path import org.eclipse.emf.ecore.EClass -import static com.google.common.base.Preconditions.checkArgument -import static extension tools.vitruv.change.atomic.id.ObjectResolutionUtil.getHierarchicUriFragment +import org.eclipse.emf.ecore.EObject import tools.vitruv.framework.vsum.internal.InternalVirtualModel import tools.vitruv.testutils.views.NonTransactionalTestView import tools.vitruv.testutils.views.TestView +import static com.google.common.base.Preconditions.checkArgument + +import static extension tools.vitruv.change.atomic.id.ObjectResolutionUtil.getHierarchicUriFragment + /** * DO NOT USE THIS CLASS! Use {@link VitruvApplicationTest} instead. *

diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend index 9d1fed3f14..e5d30778eb 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend @@ -5,7 +5,6 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import static org.junit.jupiter.api.Assertions.assertThrows -import tools.vitruv.change.atomic.uuid.UuidResolver class EdgeCaseStateChangeTest extends StateChangePropagationTest { @@ -35,19 +34,8 @@ class EdgeCaseStateChangeTest extends StateChangePropagationTest { def void testNullResources(StateBasedChangeResolutionStrategy strategyToTest) { val Resource nullResource = null assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForCreated(nullResource)] - assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceBetween(nullResource, nullResource, uuidResolver)] - assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForDeleted(nullResource, uuidResolver)] - } - - /** - * Tests invalid input: null instead of uuid resolver. - */ - @ParameterizedTest() - @MethodSource("strategiesToTest") - def void testNullUuidResolver(StateBasedChangeResolutionStrategy strategyToTest) { - val UuidResolver nullUuidResolver = null - assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceBetween(umlModel, umlCheckpoint, nullUuidResolver)] - assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForDeleted(umlModel, nullUuidResolver)] + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceBetween(nullResource, nullResource)] + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForDeleted(nullResource)] } } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend index fa95a5cd74..70fc24f1d4 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend @@ -15,6 +15,8 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Named import org.junit.jupiter.api.^extension.ExtendWith import pcm_mockup.Repository +import tools.vitruv.change.atomic.EChangeIdManager +import tools.vitruv.change.atomic.uuid.UuidResolver import tools.vitruv.change.composite.description.VitruviusChange import tools.vitruv.change.composite.recording.ChangeRecorder import tools.vitruv.testutils.RegisterMetamodelsInStandalone @@ -29,7 +31,6 @@ import static tools.vitruv.testutils.metamodels.UmlMockupCreators.uml import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util.URIUtil.createFileURI import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories -import tools.vitruv.change.atomic.uuid.UuidResolver @ExtendWith(TestProjectManager, TestLogging, RegisterMetamodelsInStandalone) abstract class StateChangePropagationTest { @@ -67,9 +68,9 @@ abstract class StateChangePropagationTest { uuidResolver = UuidResolver.create(resourceSet) checkpointResourceSet = new ResourceSetImpl().withGlobalFactories() checkpointUuidResolver = UuidResolver.create(checkpointResourceSet) - changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + changeRecorder = new ChangeRecorder(resourceSet) // Create mockup models: - resourceSet.record [ + resourceSet.record(uuidResolver) [ createPcmMockupModel() createUmlMockupModel() ] @@ -162,10 +163,12 @@ abstract class StateChangePropagationTest { } } - protected def record(T notifier, (T) => void function) { + protected def record(T notifier, UuidResolver uuidResolver, (T) => void function) { notifier.startRecording function.apply(notifier) - return notifier.endRecording + val result = notifier.endRecording + EChangeIdManager.setOrGenerateIds(result.EChanges, uuidResolver) + return result } private def Resource createCheckpoint(Resource original) { diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java index a827189aa2..4c3e0cf726 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java @@ -27,12 +27,12 @@ import allElementTypes.NonRoot; import allElementTypes.Root; -import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.change.atomic.eobject.EobjectPackage; import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; import tools.vitruv.change.atomic.root.InsertRootEObject; import tools.vitruv.change.atomic.root.RootFactory; import tools.vitruv.change.atomic.root.RootPackage; +import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.framework.views.ChangeableViewSource; import tools.vitruv.framework.views.ModifiableViewSelection; import tools.vitruv.framework.views.changederivation.DefaultStateBasedChangeResolutionStrategy; diff --git a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend index f4c1751150..ee91b947a5 100644 --- a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend +++ b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend @@ -9,10 +9,12 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.^extension.ExtendWith +import tools.vitruv.change.atomic.EChangeIdManager import tools.vitruv.change.atomic.eobject.CreateEObject import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute import tools.vitruv.change.atomic.feature.reference.ReplaceSingleValuedEReference import tools.vitruv.change.atomic.root.InsertRootEObject +import tools.vitruv.change.atomic.uuid.UuidResolver import tools.vitruv.change.composite.recording.ChangeRecorder import tools.vitruv.framework.views.View import tools.vitruv.framework.views.ViewTypeFactory @@ -33,7 +35,6 @@ import static extension edu.kit.ipd.sdq.commons.util.java.lang.IterableUtil.clai import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceUtil.getFirstRootEObject import static extension tools.vitruv.framework.vsum.VirtualModelTestUtil.* -import tools.vitruv.change.atomic.uuid.UuidResolver @ExtendWith(TestProjectManager) class VirtualModelTest { @@ -53,7 +54,7 @@ class VirtualModelTest { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ @@ -61,7 +62,7 @@ class VirtualModelTest { id = 'root' ] ] - val recordedChange = changeRecorder.endRecording + val recordedChange = changeRecorder.endRecording(uuidResolver) virtualModel.propagateChange(recordedChange) val vsumModel = virtualModel.getModelInstance(createTestModelResourceUri("")) assertThat(vsumModel.resource, containsModelOf(monitoredResource)) @@ -73,7 +74,7 @@ class VirtualModelTest { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ @@ -81,7 +82,7 @@ class VirtualModelTest { id = 'root' ] ] - val recordedChange = changeRecorder.endRecording + val recordedChange = changeRecorder.endRecording(uuidResolver) virtualModel.propagateChange(recordedChange) val sorceModel = virtualModel.getModelInstance(createTestModelResourceUri("")) val targetModel = virtualModel.getModelInstance( @@ -97,7 +98,7 @@ class VirtualModelTest { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val containedRoot = aet.Root @@ -112,7 +113,7 @@ class VirtualModelTest { resourceSet.createResource(createTestModelResourceUri("Contained")) => [ contents += containedRoot ] - val recordedChange = changeRecorder.endRecording + val recordedChange = changeRecorder.endRecording(uuidResolver) val propagatedChanges = virtualModel.propagateChange(recordedChange) val consequentialChanges = propagatedChanges.map[consequentialChanges.EChanges].flatten assertEquals(2, consequentialChanges.filter(CreateEObject).size) @@ -128,7 +129,7 @@ class VirtualModelTest { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val containedRoot = aet.Root @@ -150,7 +151,7 @@ class VirtualModelTest { resourceSet.createResource(createTestModelResourceUri("ContainedInContained")) => [ contents += containedInContainedRoot ] - val recordedChange = changeRecorder.endRecording + val recordedChange = changeRecorder.endRecording(uuidResolver) val propagatedChanges = virtualModel.propagateChange(recordedChange) val consequentialChanges = propagatedChanges.map[consequentialChanges.EChanges].flatten assertEquals(3, consequentialChanges.filter(CreateEObject).size) @@ -166,7 +167,7 @@ class VirtualModelTest { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val monitoredResource = resourceSet.createResource(createTestModelResourceUri("")) => [ @@ -174,7 +175,7 @@ class VirtualModelTest { id = 'root' ] ] - val recordedChange = changeRecorder.endRecording + val recordedChange = changeRecorder.endRecording(uuidResolver) virtualModel.propagateChange(recordedChange) val reloadedResource = new ResourceSetImpl().withGlobalFactories.getResource(createTestModelResourceUri(""), true) @@ -187,7 +188,7 @@ class VirtualModelTest { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val root = aet.Root @@ -196,7 +197,7 @@ class VirtualModelTest { id = 'root' ] ] - val recordedChange = changeRecorder.endRecording + val recordedChange = changeRecorder.endRecording(uuidResolver) virtualModel.propagateChange(recordedChange) val originalModel = virtualModel.getModelInstance(createTestModelResourceUri("")) val reloadedVirtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) @@ -206,7 +207,7 @@ class VirtualModelTest { // Propagate another change to reloaded virtual model to ensure that everything is loaded correctly changeRecorder.beginRecording root.singleValuedEAttribute = 1 - val secondRecordedChange = changeRecorder.endRecording + val secondRecordedChange = changeRecorder.endRecording(uuidResolver) val propagatedChange = reloadedVirtualModel.propagateChange(secondRecordedChange) assertEquals(1, propagatedChange.size) } @@ -217,7 +218,7 @@ class VirtualModelTest { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val root = aet.Root @@ -226,7 +227,7 @@ class VirtualModelTest { id = 'root' ] ] - val recordedChange = changeRecorder.endRecording + val recordedChange = changeRecorder.endRecording(uuidResolver) virtualModel.propagateChange(recordedChange) val originalModel = virtualModel.getModelInstance(createTestModelResourceUri("")) val reloadedVirtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) @@ -247,7 +248,7 @@ class VirtualModelTest { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories val uuidResolver = UuidResolver.create(resourceSet) - val changeRecorder = new ChangeRecorder(resourceSet, uuidResolver) + val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording val root = aet.Root @@ -257,13 +258,13 @@ class VirtualModelTest { id = 'root' ] ] - virtualModel.propagateChange(changeRecorder.endRecording) + virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) changeRecorder.beginRecording val testIntermediateUri = createTestModelResourceUri("intermediate") resourceSet.createResource(testIntermediateUri) => [ contents += root ] - virtualModel.propagateChange(changeRecorder.endRecording) + virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) // There must not be the old and the old corresponding model assertNull(virtualModel.getModelInstance(testUri)) assertNull( @@ -272,7 +273,7 @@ class VirtualModelTest { monitoredResource => [ contents += root ] - virtualModel.propagateChange(changeRecorder.endRecording) + virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) assertNull(virtualModel.getModelInstance(testIntermediateUri)) assertNull( virtualModel.getModelInstance( @@ -369,6 +370,12 @@ class VirtualModelTest { private def getPathToVirtualModelProjectFolder() { projectFolder.resolve("vsum") } + + private def endRecording(ChangeRecorder changeRecorder, UuidResolver uuidResolver) { + val change = changeRecorder.endRecording + EChangeIdManager.setOrGenerateIds(change.EChanges, uuidResolver) + return change + } def private createAndPropagateRoot(VirtualModel virtualModel, ResourceSet resourceSet, UuidResolver uuidResolver, String rootId) { virtualModel.propagateChange(resourceSet.recordChanges(uuidResolver) [ diff --git a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend index 8ac7227229..c862b30761 100644 --- a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend +++ b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend @@ -1,28 +1,27 @@ package tools.vitruv.framework.vsum +import allElementTypes.AllElementTypesPackage import allElementTypes.Root import edu.kit.ipd.sdq.activextendannotations.Utility import java.nio.file.Path import org.eclipse.emf.common.util.URI import org.eclipse.emf.ecore.resource.ResourceSet -import tools.vitruv.change.composite.description.VitruviusChange import tools.vitruv.change.atomic.EChange +import tools.vitruv.change.atomic.EChangeIdManager import tools.vitruv.change.atomic.root.InsertRootEObject +import tools.vitruv.change.atomic.uuid.UuidResolver +import tools.vitruv.change.composite.MetamodelDescriptor +import tools.vitruv.change.composite.description.VitruviusChange import tools.vitruv.change.composite.recording.ChangeRecorder +import tools.vitruv.change.correspondence.Correspondence +import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView +import tools.vitruv.change.interaction.UserInteractionFactory import tools.vitruv.change.propagation.ResourceAccess import tools.vitruv.change.propagation.impl.AbstractChangePropagationSpecification -import tools.vitruv.change.interaction.UserInteractionFactory -import tools.vitruv.framework.vsum.VirtualModelBuilder import static org.junit.jupiter.api.Assertions.assertEquals import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet -import allElementTypes.AllElementTypesPackage -import tools.vitruv.change.composite.MetamodelDescriptor -import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView -import tools.vitruv.change.correspondence.Correspondence -import tools.vitruv.change.atomic.uuid.UuidResolver - /** * Utility methods for the VSUM and view test cases. */ @@ -33,11 +32,12 @@ class VirtualModelTestUtil { * Create a recorder, start recording a resource set, apply changes, stop and return the recorded changes. */ def static VitruviusChange recordChanges(ResourceSet resourceSet, UuidResolver uuidResolver, Runnable changesToPerform) { - val recorder = new ChangeRecorder(resourceSet, uuidResolver) + val recorder = new ChangeRecorder(resourceSet) recorder.addToRecording(resourceSet) recorder.beginRecording changesToPerform.run() val result = recorder.endRecording + EChangeIdManager.setOrGenerateIds(result.EChanges, uuidResolver) recorder.close return result } From f751f99b7360f839b53f2548f96b6161ff22b29e Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Fri, 25 Nov 2022 17:12:46 +0100 Subject: [PATCH 12/19] adapt `DefaultStateBasedChangeResolutionStrategy` to return changes with elements of the actually provided resource set instead of ones from the internally used resource set --- ...ltStateBasedChangeResolutionStrategy.xtend | 100 +++++++++++++++--- .../StateBasedChangeResolutionStrategy.xtend | 24 ++++- 2 files changed, 102 insertions(+), 22 deletions(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend index 7becdc5e9f..764ba63644 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend @@ -1,7 +1,10 @@ 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 @@ -9,21 +12,28 @@ 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 */ @@ -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) { @@ -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 record(Resource resource, UuidResolver uuidResolver, ()=>void function) { - try (val changeRecorder = new ChangeRecorder(resource.resourceSet, uuidResolver)) { + private def record(Resource resource, ()=>void function) { + try (val changeRecorder = new ChangeRecorder(resource.resourceSet)) { changeRecorder.beginRecording changeRecorder.addToRecording(resource) function.apply() @@ -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 createdObjects = new HashMap() + for (eChange : change.EChanges) { + switch eChange { + CreateEObject: + eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, true) + EObjectExistenceEChange: + eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, false) + FeatureEChange: + eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, false) + } + switch eChange { + EObjectSubtractedEChange: + if (eChange.oldValue !== null) { + eChange.oldValue = getOriginalForCopy(copier, eChange.oldValue, createdObjects, false) + } + } + switch eChange { + EObjectAddedEChange: + if (eChange.newValue !== null) { + eChange.newValue = getOriginalForCopy(copier, eChange.newValue, createdObjects, true) + } + } + } + return change + } + + private def T getOriginalForCopy(ResourceCopier copier, T copy, Map 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(); + } } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend index 4ccd82a26c..0d53a3a0e4 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/StateBasedChangeResolutionStrategy.xtend @@ -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 /** @@ -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 null and must not contain proxies. * @param oldState is the current or old state of the resource, must not be null 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 null 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 null and must not contain proxies. * @return a {@link VitruviusChange} that contains the individual change sequence. */ From 54df23b95fd17b40a4c31c5d9da5140488f72853 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Fri, 25 Nov 2022 17:13:02 +0100 Subject: [PATCH 13/19] adapt test cases to UUID changes --- .../views/impl/ChangeDerivingView.xtend | 2 +- .../views/impl/IdentityMappingViewType.java | 2 +- .../framework/views/util/ResourceCopier.xtend | 54 +++++++++--- .../BasicStateChangePropagationTest.xtend | 82 +++++++++---------- .../StateChangePropagationTest.xtend | 8 +- .../views/util/XmiIdEdgeCaseTest.java | 4 +- 6 files changed, 91 insertions(+), 61 deletions(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend index cad27c6ce2..0f6e0d8b4c 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeDerivingView.xtend @@ -49,7 +49,7 @@ class ChangeDerivingView implements ModifiableView, CommittableView { private def setupReferenceState() { originalStateViewResourceSet = new ResourceSetImpl - ResourceCopier.copyViewResources(view.viewResourceSet.resources, originalStateViewResourceSet) + new ResourceCopier().copyViewResources(view.viewResourceSet.resources, originalStateViewResourceSet) originalStateResourceMapping = new HashMap view.viewResourceSet.resources.forEach[resource | originalStateResourceMapping.put(resource, originalStateViewResourceSet.resources.findFirst[URI === resource.URI])] } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java index 77aa89515e..14fd88e584 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java @@ -55,7 +55,7 @@ public void updateView(ModifiableView view) { List resourcesWithSelectedElements = viewSources.stream() .filter(resource -> resource.getContents().stream().anyMatch(selection::isViewObjectSelected)) .collect(Collectors.toList()); - Map mapping = ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, + Map mapping = new ResourceCopier().copyViewSourceResources(resourcesWithSelectedElements, viewResourceSet, selection::isViewObjectSelected); view.getViewSource().getUuidResolver().resolveResources(mapping, viewUuidResolver); }); diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/util/ResourceCopier.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/util/ResourceCopier.xtend index 67fd5aa12a..66fb0b7473 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/util/ResourceCopier.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/util/ResourceCopier.xtend @@ -1,6 +1,5 @@ package tools.vitruv.framework.views.util -import edu.kit.ipd.sdq.activextendannotations.Utility import java.nio.file.Files import java.util.HashMap import java.util.Map @@ -19,8 +18,9 @@ import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util /** * A utility class to create copies of resources. */ -@Utility class ResourceCopier { + var Map copyToOriginalMapping = new HashMap(); + /** * Indicates whether the given resource requires a full copy. * If false is returned, the resource supports a partial copy, @@ -39,7 +39,7 @@ class ResourceCopier { * @param rootElementPredicate A predicate to include only a subset of the root elements of the originalResources * @returns A mapping from each original resource to its copy */ - static def Map copyViewSourceResources(Iterable originalResources, + def Map copyViewSourceResources(Iterable originalResources, ResourceSet newResourceSet, (EObject)=>Boolean rootElementPredicate) { // do not copy xmi:id for view source resources as the change recorder does not record them // and thus the information will be lost when propagating changes back to the view source @@ -53,7 +53,7 @@ class ResourceCopier { * @param newResourceSet The resource set to which the copies are attached * @returns A mapping from each original resource to its copy */ - static def Map copyViewResources(Iterable originalResources, + def Map copyViewResources(Iterable originalResources, ResourceSet newResourceSet) { // copy xmi:id for view resources such that an exact copy is created // this is e.g. necessary for change deriving strategies that rely on identifiers @@ -67,12 +67,22 @@ class ResourceCopier { * @param newResourceSet The resource set to which the copy is attached * @returns The newly created copy of the resource */ - static def Resource copyViewResource(Resource originalResource, ResourceSet newResourceSet) { + def Resource copyViewResource(Resource originalResource, ResourceSet newResourceSet) { val mapping = copyViewResources(#[originalResource], newResourceSet) return mapping.get(originalResource) } - private static def Map copyResources(Iterable originalResources, + /** + * Returns the element to which the given element is its copy, + * or {@code null} if no such element exists. + * + * @param copy is the copied {@link EObject}. + */ + def EObject getOriginalForCopy(EObject copy) { + copyToOriginalMapping.get(copy) + } + + private def Map copyResources(Iterable originalResources, ResourceSet newResourceSet, Boolean copyXmlIds, (EObject)=>Boolean rootElementPredicate) { val resourceMapping = new HashMap() for (umlResource : originalResources.filter[isWritableUmlResource].toList) { @@ -104,7 +114,7 @@ class ResourceCopier { * To circumvent the issue, we store the UML model to a temporary resource and reload it, * because save/load properly handles the situation not covered by the copier. */ - private static def Resource copyUmlResource(Resource originalResource, ResourceSet newResourceSet) { + private def Resource copyUmlResource(Resource originalResource, ResourceSet newResourceSet) { val originalURI = originalResource.URI val tempFilePath = Files.createTempFile(null, "." + originalURI.fileExtension) val tempURI = URI.createFileURI(tempFilePath.toString) @@ -115,6 +125,7 @@ class ResourceCopier { copiedResource.URI = originalURI Files.delete(tempFilePath) EcoreUtil.resolveAll(copiedResource) + registerInMappingForUmlResources(originalResource, copiedResource) return copiedResource } @@ -126,7 +137,7 @@ class ResourceCopier { * We must NOT copy these elements but only copy the containing element and then resolve it to avoid * element duplication. */ - private static def Map copyResourcesInternal(Iterable originalResources, + private def Map copyResourcesInternal(Iterable originalResources, ResourceSet newResourceSet, Boolean copyXmlIds, (EObject)=>Boolean rootElementPredicate) { val copier = new Copier(true) var resourceMapping = new HashMap() @@ -143,7 +154,9 @@ class ResourceCopier { originalResource.URI) val selectedRootElements = originalResource.contents.filter[rootElementPredicate.apply(it)] val mappedRootElements = selectedRootElements.map [ - checkNotNull(copier.get(it), "corresponding object for %s is null", it) + val result = checkNotNull(copier.get(it), "corresponding object for %s is null", it) + registerInMapping(it, copier) + return result ] copiedResource.contents.addAll(mappedRootElements) newResourceSet.resources += copiedResource @@ -163,7 +176,7 @@ class ResourceCopier { return resourceMapping } - private static def boolean isContainedInOtherThanOwnResource(EObject eObject, Iterable resources) { + private def boolean isContainedInOtherThanOwnResource(EObject eObject, Iterable resources) { val rootContainerResource = EcoreUtil.getRootContainer(eObject).eResource return rootContainerResource !== eObject.eResource && resources.contains(rootContainerResource) } @@ -176,7 +189,7 @@ class ResourceCopier { * Copies the IDs of the source's elements to the target. * It is expected that both resources are in an identical state. */ - private static def void copyUmlIds(XMLResource source, XMLResource target) { + private def void copyUmlIds(XMLResource source, XMLResource target) { val sourceIterator = source.allContents val targetIterator = target.allContents while (sourceIterator.hasNext && targetIterator.hasNext) { @@ -189,4 +202,23 @@ class ResourceCopier { checkState(!sourceIterator.hasNext, "source uml resource has too many elements") checkState(!targetIterator.hasNext, "target uml resource has too many elements") } + + private def void registerInMapping(EObject original, EcoreUtil.Copier copier) { + copyToOriginalMapping.put(copier.get(original), original) + original.eContents.forEach [ registerInMapping(it, copier) ] + } + + private def void registerInMappingForUmlResources(Resource originalResource, Resource copiedResource) { + val originalIterator = originalResource.allContents + val copyIterator = copiedResource.allContents + while (originalIterator.hasNext && copyIterator.hasNext) { + val original = originalIterator.next + val copy = copyIterator.next + checkState(original.eClass === copy.eClass, "non matching elements %s and %s", original, + copy) + copyToOriginalMapping.put(copy, original) + } + checkState(!originalIterator.hasNext, "original uml resource has too many elements") + checkState(!copyIterator.hasNext, "copied uml resource has too many elements") + } } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend index 26a1040669..d39d19a1f6 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend @@ -6,15 +6,17 @@ import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource +import tools.vitruv.change.atomic.EChangeIdManager import tools.vitruv.change.atomic.eobject.CreateEObject import tools.vitruv.change.atomic.eobject.DeleteEObject import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute import tools.vitruv.change.atomic.root.InsertRootEObject import tools.vitruv.change.atomic.root.RemoveRootEObject +import tools.vitruv.change.atomic.uuid.UuidResolver import tools.vitruv.testutils.Capture -import static org.hamcrest.MatcherAssert.assertThat import static org.hamcrest.CoreMatchers.instanceOf +import static org.hamcrest.MatcherAssert.assertThat import static org.junit.jupiter.api.Assertions.assertEquals import static org.junit.jupiter.api.Assertions.assertTrue import static tools.vitruv.testutils.matchers.ModelMatchers.containsModelOf @@ -22,7 +24,6 @@ import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories import static extension tools.vitruv.testutils.Capture.operator_doubleGreaterThan -import tools.vitruv.change.atomic.uuid.UuidResolver class BasicStateChangePropagationTest extends StateChangePropagationTest { private def getTestUri() { @@ -39,17 +40,19 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] ] + // Create empty resource to apply generated changes to + val validationResourceSet = new ResourceSetImpl() + val validationUuidResolver = UuidResolver.create(validationResourceSet) + val changes = strategyToTest.getChangeSequenceForCreated(modelResource) + EChangeIdManager.setOrGenerateIds(changes.EChanges, uuidResolver) + changes.unresolve().resolveAndApply(validationUuidResolver) + assertEquals(3, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) assertEquals(1, changes.EChanges.filter(CreateEObject).size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - // Create empty resource to apply generated changes to - val validationResourceSet = new ResourceSetImpl() - val validationUuidResolver = UuidResolver.create(validationResourceSet) - changes.unresolve().resolveAndApply(validationUuidResolver) - modelResource.save(null) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(modelResource)) @@ -60,7 +63,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { @MethodSource("strategiesToTest") def void deleteResource(StateBasedChangeResolutionStrategy strategyToTest) { val modelResource = new Capture - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(testUri) => [ contents += aet.Root => [ id = "Root" @@ -74,13 +77,14 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationUuidResolver = UuidResolver.create(validationResourceSet) uuidResolver.resolveResource(-modelResource, oldResource, validationUuidResolver) - val changes = strategyToTest.getChangeSequenceForDeleted(-modelResource, uuidResolver) + val changes = strategyToTest.getChangeSequenceForDeleted(-modelResource) + EChangeIdManager.setOrGenerateIds(changes.EChanges, uuidResolver) + changes.unresolve().resolveAndApply(validationUuidResolver) + assertEquals(2, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) assertEquals(1, changes.EChanges.filter(DeleteEObject).size) - changes.unresolve().resolveAndApply(validationUuidResolver) - assertEquals(1, validationResourceSet.resources.size) assertTrue(validationResourceSet.resources.get(0).contents.empty) } @@ -90,7 +94,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { @MethodSource("strategiesToTest") def void replaceRootElement(StateBasedChangeResolutionStrategy strategyToTest) { val modelResource = new Capture - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(testUri) => [ contents += aet.Root => [ id = "Root" @@ -104,15 +108,14 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationUuidResolver = UuidResolver.create(validationResourceSet) uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) - (-modelResource).record [ + (-modelResource).record(uuidResolver) [ contents.clear() contents += aet.Root => [ id = "Root2" ] ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) - changes.unresolve().resolveAndApply(validationUuidResolver) + strategyToTest.getChangeSequenceBetweenAndApply(-modelResource, oldState, validationUuidResolver) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -124,7 +127,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { def void changeRootElementFeature(StateBasedChangeResolutionStrategy strategyToTest) { val modelResource = new Capture val root = aet.Root - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(testUri) => [ contents += root => [ id = "Root" @@ -138,16 +141,15 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationUuidResolver = UuidResolver.create(validationResourceSet) uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) - resourceSet.record [ + resourceSet.record(uuidResolver) [ root.singleValuedEAttribute = 2 ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) + val changes = strategyToTest.getChangeSequenceBetweenAndApply(-modelResource, oldState, validationUuidResolver) + EChangeIdManager.setOrGenerateIds(changes.EChanges, validationUuidResolver) assertEquals(1, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationUuidResolver) - assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) } @@ -158,7 +160,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { def void changeRootElementId(DefaultStateBasedChangeResolutionStrategy strategyToTest) { val modelResource = new Capture val root = aet.Root - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(testUri) => [ contents += root => [ id = "Root" @@ -172,11 +174,11 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationUuidResolver = UuidResolver.create(validationResourceSet) uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) - resourceSet.record [ + resourceSet.record(uuidResolver) [ root.id = "Root2" ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) + var changes = strategyToTest.getChangeSequenceBetweenAndApply(-modelResource, oldState, validationUuidResolver) switch (strategyToTest.useIdentifiers) { case ONLY, case WHEN_AVAILABLE: { @@ -196,8 +198,6 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { } } - changes.unresolve().resolveAndApply(validationUuidResolver) - assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) } @@ -209,7 +209,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val modelResource = new Capture val root = aet.Root val containedRoot = aet.Root - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(testUri) => [ contents += root => [ id = "Root" @@ -227,16 +227,14 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationUuidResolver = UuidResolver.create(validationResourceSet) uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) - resourceSet.record [ + resourceSet.record(uuidResolver) [ containedRoot.singleValuedEAttribute = 1 ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) + val changes = strategyToTest.getChangeSequenceBetweenAndApply(-modelResource, oldState, validationUuidResolver) assertEquals(1, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationUuidResolver) - assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) } @@ -248,7 +246,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val modelResource = new Capture val root = aet.Root val containedRoot = aet.Root - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(testUri) => [ contents += root => [ id = "Root" @@ -266,11 +264,11 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationUuidResolver = UuidResolver.create(validationResourceSet) uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) - resourceSet.record [ + resourceSet.record(uuidResolver) [ containedRoot.id = "ContainedRoot2" ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) + val changes = strategyToTest.getChangeSequenceBetweenAndApply(-modelResource, oldState, validationUuidResolver) switch (strategyToTest.useIdentifiers) { case ONLY, case WHEN_AVAILABLE: { @@ -290,8 +288,6 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { } } - changes.unresolve().resolveAndApply(validationUuidResolver) - assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) } @@ -302,7 +298,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { def void moveResource(StateBasedChangeResolutionStrategy strategyToTest) { val modelResource = new Capture val root = aet.Root - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(testUri) => [ contents += root => [ id = "Root" @@ -317,19 +313,17 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) val movedResourceUri = getModelURI("moved.allElementTypes") - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(movedResourceUri) => [ contents += root ] >> modelResource ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) + val changes = strategyToTest.getChangeSequenceBetweenAndApply(-modelResource, oldState, validationUuidResolver) assertEquals(2, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) - changes.unresolve().resolveAndApply(validationUuidResolver) - (-modelResource).save(null) assertEquals(2, validationResourceSet.resources.size) assertThat(validationResourceSet.getResource(movedResourceUri, false), containsModelOf(-modelResource)) @@ -341,7 +335,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { def void moveResourceAndChangeRootFeature(StateBasedChangeResolutionStrategy strategyToTest) { val modelResource = new Capture val root = aet.Root - resourceSet.record [ + resourceSet.record(uuidResolver) [ createResource(testUri) => [ contents += root => [ id = "Root" @@ -356,7 +350,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { uuidResolver.resolveResource(-modelResource, oldState, validationUuidResolver) val movedResourceUri = getModelURI("moved.allElementTypes") - resourceSet.record [ + resourceSet.record(uuidResolver) [ (-modelResource).contents -= root root.singleValuedEAttribute = 2 createResource(movedResourceUri) => [ @@ -364,14 +358,12 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { ] >> modelResource ] - val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState, validationUuidResolver) + val changes = strategyToTest.getChangeSequenceBetweenAndApply(-modelResource, oldState, validationUuidResolver) assertEquals(3, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationUuidResolver) - (-modelResource).save(null) assertEquals(2, validationResourceSet.resources.size) assertThat(validationResourceSet.getResource(movedResourceUri, false), containsModelOf(-modelResource)) diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend index 70fc24f1d4..2638463c17 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend @@ -104,7 +104,7 @@ abstract class StateChangePropagationTest { protected def compareChanges(Resource model, Resource checkpoint, StateBasedChangeResolutionStrategy strategyToTest) { model.save(null) val deltaBasedChange = resourceSet.endRecording - val stateBasedChange = strategyToTest.getChangeSequenceBetween(model, checkpoint, checkpointUuidResolver) + val stateBasedChange = strategyToTest.getChangeSequenceBetweenAndApply(model, checkpoint, checkpointUuidResolver) assertNotNull(stateBasedChange) val message = getTextualRepresentation(stateBasedChange, deltaBasedChange) val stateBasedChangedObjects = stateBasedChange.affectedAndReferencedEObjects @@ -180,4 +180,10 @@ abstract class StateChangePropagationTest { protected def URI getModelURI(String modelFileName) { return testProjectFolder.resolve("model").resolve(modelFileName).toFile().createFileURI() } + + protected def getChangeSequenceBetweenAndApply(StateBasedChangeResolutionStrategy strategy, Resource newState, Resource oldState, UuidResolver oldStateUuidResolver) { + val changes = strategy.getChangeSequenceBetween(newState, oldState) + EChangeIdManager.setOrGenerateIds(changes.EChanges, oldStateUuidResolver) + return changes.unresolve.resolveAndApply(oldStateUuidResolver) + } } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java index ce2cf79b28..aa87fc1c44 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java @@ -45,7 +45,7 @@ void setup(@TestProject Path testProjectFolder) { public void testSingleResourceCopy() { Pair> umlResourcePair = createPopulatedUmlResourceAndIdMapping("my"); ResourceSet copyResourceSet = new ResourceSetImpl(); - XMLResource copiedModel = (XMLResource) ResourceCopier.copyViewResource(umlResourcePair.get0(), + XMLResource copiedModel = (XMLResource) new ResourceCopier().copyViewResource(umlResourcePair.get0(), copyResourceSet); validateIds(copiedModel, umlResourcePair.get1()); } @@ -57,7 +57,7 @@ public void testMultiResourceCopy() { Pair> umlResourcePair3 = createPopulatedUmlResourceAndIdMapping("my3"); ResourceSet copyResourceSet = new ResourceSetImpl(); - Map copiedModels = ResourceCopier.copyViewResources( + Map copiedModels = new ResourceCopier().copyViewResources( List.of(umlResourcePair.get0(), umlResourcePair2.get0(), umlResourcePair3.get0()), copyResourceSet); XMLResource copiedModel = (XMLResource) copiedModels.get(umlResourcePair.get0()); XMLResource copiedModel2 = (XMLResource) copiedModels.get(umlResourcePair2.get0()); From 31072614e22a8b36e90ff798ac245163307665fb Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Fri, 25 Nov 2022 18:18:32 +0100 Subject: [PATCH 14/19] fix view test for UUID resolver --- .../framework/views/impl/ChangeDerivingViewTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java index 4c3e0cf726..07347e499c 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java @@ -27,11 +27,14 @@ import allElementTypes.NonRoot; import allElementTypes.Root; +import tools.vitruv.change.atomic.EChange; import tools.vitruv.change.atomic.eobject.EobjectPackage; import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; +import tools.vitruv.change.atomic.resolve.EChangeResolverAndApplicator; import tools.vitruv.change.atomic.root.InsertRootEObject; import tools.vitruv.change.atomic.root.RootFactory; import tools.vitruv.change.atomic.root.RootPackage; +import tools.vitruv.change.atomic.uuid.UuidResolver; import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.framework.views.ChangeableViewSource; import tools.vitruv.framework.views.ModifiableViewSelection; @@ -47,6 +50,8 @@ public class ChangeDerivingViewTest { ChangeableViewSource mockChangeableViewSource; @Mock ModifiableViewSelection mockViewSelection; + @Mock + UuidResolver uuidResolver; @BeforeEach public void initializeMocks() { @@ -209,6 +214,10 @@ public void commitChanges() throws Exception { InsertRootEObject expectedChange = RootFactory.eINSTANCE.createInsertRootEObject(); expectedChange.setNewValue(root); expectedChange.setUri(testResourceUriString); + // validation expects the change to represent the new state, thus apply it + for (EChange change : argument.getValue().getEChanges()) { + EChangeResolverAndApplicator.applyForward(change, uuidResolver); + } assertThat(argument.getValue().getEChanges().size(), is(3)); // Create, Insert, ReplaceId assertThat(argument.getValue().getEChanges().get(1), equalsDeeply(expectedChange, From b023ed57a881509a829ec288f7128b0b04f21b79 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Sun, 5 Feb 2023 12:58:10 +0100 Subject: [PATCH 15/19] use ids in view, uuids in v-sum --- ...ltStateBasedChangeResolutionStrategy.xtend | 107 +++++------------- .../framework/views/impl/BasicView.xtend | 12 +- .../views/impl/ChangeRecordingView.xtend | 21 +++- .../views/impl/IdentityMappingViewType.java | 51 ++++++--- .../framework/views/impl/ModifiableView.xtend | 3 +- .../framework/views/util/ResourceCopier.xtend | 54 ++------- .../vsum/internal/ResourceRepositoryImpl.java | 4 +- .../BasicStateChangePropagationTest.xtend | 25 ++-- .../StateChangePropagationTest.xtend | 3 +- .../framework/views/impl/BasicViewTest.java | 2 +- .../views/impl/ChangeDerivingViewTest.java | 3 +- .../views/impl/ChangeRecordingViewTest.java | 2 +- .../views/util/XmiIdEdgeCaseTest.java | 4 +- .../framework/vsum/VirtualModelTest.xtend | 4 +- .../framework/vsum/VirtualModelTestUtil.xtend | 4 +- 15 files changed, 123 insertions(+), 176 deletions(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend index 16baddbb7f..9d1fcb7269 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/changederivation/DefaultStateBasedChangeResolutionStrategy.xtend @@ -1,10 +1,7 @@ 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 @@ -12,24 +9,21 @@ 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.ResourceSet 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.atomic.EChangeIdManager +import tools.vitruv.change.atomic.id.IdResolver +import tools.vitruv.change.composite.description.VitruviusChange 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 static extension tools.vitruv.change.atomic.resolve.EChangeIdResolverAndApplicator.applyBackward +import static extension tools.vitruv.change.atomic.resolve.EChangeIdResolverAndApplicator.applyForward /** * This default strategy for diff based state changes uses EMFCompare to resolve a @@ -66,15 +60,13 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol newState.checkNoProxies("new state") oldState.checkNoProxies("old state") val monitoredResourceSet = new ResourceSetImpl() - val resourceCopier = new ResourceCopier() - val currentStateCopy = resourceCopier.copyViewResource(oldState, monitoredResourceSet) - val change = currentStateCopy.record [ + val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet) + return currentStateCopy.record [ if (oldState.URI != newState.URI) { currentStateCopy.URI = newState.URI } compareStatesAndReplayChanges(newState, currentStateCopy) ] - return transformChangeToOriginalResourceSet(change, resourceCopier) } override getChangeSequenceForCreated(Resource newState) { @@ -85,10 +77,9 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol val monitoredResourceSet = new ResourceSetImpl() val newResource = monitoredResourceSet.createResource(newState.URI) newResource.contents.clear() - val change = newResource.record [ + return newResource.record [ newResource.contents += EcoreUtil.copyAll(newState.contents) ] - return transformChangeToOriginalResourceSet(change, new ResourceCopier()); } override getChangeSequenceForDeleted(Resource oldState) { @@ -96,22 +87,33 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol oldState.checkNoProxies("old state") // Setup resolver and copy state: val monitoredResourceSet = new ResourceSetImpl() - val resourceCopier = new ResourceCopier() - val currentStateCopy = resourceCopier.copyViewResource(oldState, monitoredResourceSet) - val change = currentStateCopy.record [ + val currentStateCopy = ResourceCopier.copyViewResource(oldState, monitoredResourceSet) + return currentStateCopy.record [ currentStateCopy.contents.clear() ] - return transformChangeToOriginalResourceSet(change, resourceCopier) } private def record(Resource resource, ()=>void function) { - try (val changeRecorder = new ChangeRecorder(resource.resourceSet)) { + try (val changeRecorder = new ChangeRecorder(resource.resourceSet)) { changeRecorder.beginRecording changeRecorder.addToRecording(resource) function.apply() - return changeRecorder.endRecording.unresolve + val recordedChanges = changeRecorder.endRecording + assignIds(recordedChanges, resource.resourceSet) + return recordedChanges.unresolve } } + + private def void assignIds(VitruviusChange recordedChange, ResourceSet resourceSet) { + val changes = recordedChange.EChanges + val idResolver = IdResolver.create(resourceSet) + val eChangeIdManager = new EChangeIdManager(idResolver) + changes.toList.reverseView.forEach[applyBackward] + changes.forEach[ change | + eChangeIdManager.setOrGenerateIds(change) + change.applyForward(idResolver) + ] + } /** * Compares states using EMFCompare and replays the changes to the current state. @@ -129,61 +131,4 @@ 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 createdObjects = new HashMap() - for (eChange : change.EChanges) { - switch eChange { - CreateEObject: - eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, true) - EObjectExistenceEChange: - eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, false) - FeatureEChange: - eChange.affectedEObject = getOriginalForCopy(copier, eChange.affectedEObject, createdObjects, false) - } - switch eChange { - EObjectSubtractedEChange: - if (eChange.oldValue !== null) { - eChange.oldValue = getOriginalForCopy(copier, eChange.oldValue, createdObjects, false) - } - } - switch eChange { - EObjectAddedEChange: - if (eChange.newValue !== null) { - eChange.newValue = getOriginalForCopy(copier, eChange.newValue, createdObjects, true) - } - } - } - return change - } - - private def T getOriginalForCopy(ResourceCopier copier, T copy, Map 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(); - } } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend index c3d4074592..a73a0c50d5 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/BasicView.xtend @@ -9,6 +9,8 @@ import org.eclipse.emf.ecore.resource.Resource import org.eclipse.emf.ecore.resource.ResourceSet import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl import org.eclipse.xtend.lib.annotations.Accessors +import tools.vitruv.change.composite.description.PropagatedChange +import tools.vitruv.change.composite.description.VitruviusChange import tools.vitruv.change.composite.propagation.ChangePropagationListener import tools.vitruv.framework.views.ChangeableViewSource import tools.vitruv.framework.views.ViewSelection @@ -19,9 +21,6 @@ import static com.google.common.base.Preconditions.checkArgument import static com.google.common.base.Preconditions.checkState import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories -import tools.vitruv.change.composite.description.PropagatedChange -import tools.vitruv.change.composite.description.VitruviusChange -import tools.vitruv.change.atomic.uuid.UuidResolver package class BasicView implements ModifiableView, ChangePropagationListener { @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) @@ -32,8 +31,6 @@ package class BasicView implements ModifiableView, ChangePropagationListener { var ChangeableViewSource viewSource @Accessors(PROTECTED_GETTER) var ResourceSet viewResourceSet - @Accessors(PROTECTED_GETTER) - val UuidResolver uuidResolver boolean modelChanged @Accessors(PROTECTED_SETTER) boolean viewChanged @@ -49,7 +46,6 @@ package class BasicView implements ModifiableView, ChangePropagationListener { this.selection = selection viewSource.addChangePropagationListener(this) viewResourceSet = new ResourceSetImpl().withGlobalFactories - uuidResolver = UuidResolver.create(viewResourceSet) update } @@ -137,8 +133,8 @@ package class BasicView implements ModifiableView, ChangePropagationListener { ] } - override modifyContents((ResourceSet, UuidResolver)=>void modificationFunction) { - modificationFunction.apply(viewResourceSet, uuidResolver) + override modifyContents((ResourceSet)=>void modificationFunction) { + modificationFunction.apply(viewResourceSet) } override withChangeRecordingTrait() { diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend index 1727f3a887..d8c0938d6e 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ChangeRecordingView.xtend @@ -1,6 +1,10 @@ package tools.vitruv.framework.views.impl +import java.util.List import org.eclipse.xtend.lib.annotations.Delegate +import tools.vitruv.change.atomic.EChange +import tools.vitruv.change.atomic.EChangeIdManager +import tools.vitruv.change.atomic.id.IdResolver import tools.vitruv.change.composite.recording.ChangeRecorder import tools.vitruv.framework.views.CommittableView import tools.vitruv.framework.views.View @@ -9,6 +13,9 @@ import tools.vitruv.framework.views.changederivation.StateBasedChangeResolutionS import static com.google.common.base.Preconditions.checkArgument import static com.google.common.base.Preconditions.checkState +import static extension tools.vitruv.change.atomic.resolve.EChangeIdResolverAndApplicator.applyBackward +import static extension tools.vitruv.change.atomic.resolve.EChangeIdResolverAndApplicator.applyForward + /** * A {@link View} that records changes to its resources and allows to propagate them * back to the underlying models using the {@link #commitChanges} method. @@ -36,11 +43,21 @@ class ChangeRecordingView implements ModifiableView, CommittableView { changeRecorder.addToRecording(view.viewResourceSet) changeRecorder.beginRecording() } + def private void assignIds(List changes) { + val idResolver = IdResolver.create(view.viewResourceSet) + val idManager = new EChangeIdManager(idResolver) + changes.toList.reverseView.forEach[applyBackward] + changes.forEach[ + idManager.setOrGenerateIds(it) + it.applyForward(idResolver) + ] + } override commitChanges() { view.checkNotClosed() - changeRecorder.endRecording() - view.viewType.commitViewChanges(this, changeRecorder.change) + val recordedChange = changeRecorder.endRecording() + assignIds(recordedChange.EChanges) + view.viewType.commitViewChanges(this, recordedChange) view.viewChanged = false changeRecorder.beginRecording() } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java index 14fd88e584..1d489e94df 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java @@ -1,6 +1,7 @@ package tools.vitruv.framework.views.impl; import static com.google.common.base.Preconditions.checkArgument; +import static edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories; import java.util.Collection; import java.util.List; @@ -8,16 +9,24 @@ import java.util.stream.Collectors; import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import tools.vitruv.change.atomic.EChangeUuidManager; +import tools.vitruv.change.atomic.id.IdResolver; +import tools.vitruv.change.atomic.uuid.UuidResolver; +import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.framework.views.ChangeableViewSource; +import tools.vitruv.framework.views.View; import tools.vitruv.framework.views.ViewSelection; +import tools.vitruv.framework.views.ViewSource; import tools.vitruv.framework.views.selectors.DirectViewElementSelector; import tools.vitruv.framework.views.util.ResourceCopier; /** - * A view type that allows creating views based on a basic element-wise selection mechanism - * and providing a one-to-one (identity) mapping of elements within the {@link ViewSource} - * to a created {@link View}. + * A view type that allows creating views based on a basic element-wise + * selection mechanism and providing a one-to-one (identity) mapping of elements + * within the {@link ViewSource} to a created {@link View}. */ public class IdentityMappingViewType extends AbstractViewType { public IdentityMappingViewType(String name) { @@ -45,19 +54,33 @@ public ModifiableView createView(DirectViewElementSelector selector) { @Override public void updateView(ModifiableView view) { - view.modifyContents((viewResourceSet, viewUuidResolver) -> { + view.modifyContents((viewResourceSet) -> { viewResourceSet.getResources().forEach(Resource::unload); viewResourceSet.getResources().clear(); - viewUuidResolver.endTransaction(); - - Collection viewSources = view.getViewSource().getViewSourceModels(); - ViewSelection selection = view.getSelection(); - List resourcesWithSelectedElements = viewSources.stream() - .filter(resource -> resource.getContents().stream().anyMatch(selection::isViewObjectSelected)) - .collect(Collectors.toList()); - Map mapping = new ResourceCopier().copyViewSourceResources(resourcesWithSelectedElements, - viewResourceSet, selection::isViewObjectSelected); - view.getViewSource().getUuidResolver().resolveResources(mapping, viewUuidResolver); + createViewResources(view, viewResourceSet); }); } + + @Override + public void commitViewChanges(ModifiableView view, VitruviusChange viewChange) { + ResourceSet viewSourceCopyResourceSet = withGlobalFactories(new ResourceSetImpl()); + IdResolver viewSourceCopyIdResolver = IdResolver.create(viewSourceCopyResourceSet); + UuidResolver viewSourceCopyUuidResolver = UuidResolver.create(viewSourceCopyResourceSet); + Map mapping = createViewResources(view, viewSourceCopyResourceSet); + view.getViewSource().getUuidResolver().resolveResources(mapping, viewSourceCopyUuidResolver); + + VitruviusChange resolvedChange = viewChange.unresolve().resolveAndApply(viewSourceCopyIdResolver); + EChangeUuidManager.setOrGenerateIds(resolvedChange.getEChanges(), viewSourceCopyUuidResolver); + view.getViewSource().propagateChange(resolvedChange.unresolve()); + } + + private Map createViewResources(ModifiableView view, ResourceSet viewResourceSet) { + Collection viewSources = view.getViewSource().getViewSourceModels(); + ViewSelection selection = view.getSelection(); + List resourcesWithSelectedElements = viewSources.stream() + .filter(resource -> resource.getContents().stream().anyMatch(selection::isViewObjectSelected)) + .collect(Collectors.toList()); + return ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, viewResourceSet, + selection::isViewObjectSelected); + } } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend index c64e6e180d..2d667d3e5e 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/ModifiableView.xtend @@ -3,13 +3,12 @@ package tools.vitruv.framework.views.impl import org.eclipse.emf.ecore.resource.ResourceSet import tools.vitruv.framework.views.ChangeableViewSource import tools.vitruv.framework.views.View -import tools.vitruv.change.atomic.uuid.UuidResolver /** * A view whose contents can be modified, in particular by a view type implementation. */ interface ModifiableView extends View { - def void modifyContents((ResourceSet, UuidResolver)=>void modificationFunction); + def void modifyContents((ResourceSet)=>void modificationFunction); def ChangeableViewSource getViewSource() } diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/util/ResourceCopier.xtend b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/util/ResourceCopier.xtend index 66fb0b7473..67fd5aa12a 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/util/ResourceCopier.xtend +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/util/ResourceCopier.xtend @@ -1,5 +1,6 @@ package tools.vitruv.framework.views.util +import edu.kit.ipd.sdq.activextendannotations.Utility import java.nio.file.Files import java.util.HashMap import java.util.Map @@ -18,9 +19,8 @@ import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.common.util /** * A utility class to create copies of resources. */ +@Utility class ResourceCopier { - var Map copyToOriginalMapping = new HashMap(); - /** * Indicates whether the given resource requires a full copy. * If false is returned, the resource supports a partial copy, @@ -39,7 +39,7 @@ class ResourceCopier { * @param rootElementPredicate A predicate to include only a subset of the root elements of the originalResources * @returns A mapping from each original resource to its copy */ - def Map copyViewSourceResources(Iterable originalResources, + static def Map copyViewSourceResources(Iterable originalResources, ResourceSet newResourceSet, (EObject)=>Boolean rootElementPredicate) { // do not copy xmi:id for view source resources as the change recorder does not record them // and thus the information will be lost when propagating changes back to the view source @@ -53,7 +53,7 @@ class ResourceCopier { * @param newResourceSet The resource set to which the copies are attached * @returns A mapping from each original resource to its copy */ - def Map copyViewResources(Iterable originalResources, + static def Map copyViewResources(Iterable originalResources, ResourceSet newResourceSet) { // copy xmi:id for view resources such that an exact copy is created // this is e.g. necessary for change deriving strategies that rely on identifiers @@ -67,22 +67,12 @@ class ResourceCopier { * @param newResourceSet The resource set to which the copy is attached * @returns The newly created copy of the resource */ - def Resource copyViewResource(Resource originalResource, ResourceSet newResourceSet) { + static def Resource copyViewResource(Resource originalResource, ResourceSet newResourceSet) { val mapping = copyViewResources(#[originalResource], newResourceSet) return mapping.get(originalResource) } - /** - * Returns the element to which the given element is its copy, - * or {@code null} if no such element exists. - * - * @param copy is the copied {@link EObject}. - */ - def EObject getOriginalForCopy(EObject copy) { - copyToOriginalMapping.get(copy) - } - - private def Map copyResources(Iterable originalResources, + private static def Map copyResources(Iterable originalResources, ResourceSet newResourceSet, Boolean copyXmlIds, (EObject)=>Boolean rootElementPredicate) { val resourceMapping = new HashMap() for (umlResource : originalResources.filter[isWritableUmlResource].toList) { @@ -114,7 +104,7 @@ class ResourceCopier { * To circumvent the issue, we store the UML model to a temporary resource and reload it, * because save/load properly handles the situation not covered by the copier. */ - private def Resource copyUmlResource(Resource originalResource, ResourceSet newResourceSet) { + private static def Resource copyUmlResource(Resource originalResource, ResourceSet newResourceSet) { val originalURI = originalResource.URI val tempFilePath = Files.createTempFile(null, "." + originalURI.fileExtension) val tempURI = URI.createFileURI(tempFilePath.toString) @@ -125,7 +115,6 @@ class ResourceCopier { copiedResource.URI = originalURI Files.delete(tempFilePath) EcoreUtil.resolveAll(copiedResource) - registerInMappingForUmlResources(originalResource, copiedResource) return copiedResource } @@ -137,7 +126,7 @@ class ResourceCopier { * We must NOT copy these elements but only copy the containing element and then resolve it to avoid * element duplication. */ - private def Map copyResourcesInternal(Iterable originalResources, + private static def Map copyResourcesInternal(Iterable originalResources, ResourceSet newResourceSet, Boolean copyXmlIds, (EObject)=>Boolean rootElementPredicate) { val copier = new Copier(true) var resourceMapping = new HashMap() @@ -154,9 +143,7 @@ class ResourceCopier { originalResource.URI) val selectedRootElements = originalResource.contents.filter[rootElementPredicate.apply(it)] val mappedRootElements = selectedRootElements.map [ - val result = checkNotNull(copier.get(it), "corresponding object for %s is null", it) - registerInMapping(it, copier) - return result + checkNotNull(copier.get(it), "corresponding object for %s is null", it) ] copiedResource.contents.addAll(mappedRootElements) newResourceSet.resources += copiedResource @@ -176,7 +163,7 @@ class ResourceCopier { return resourceMapping } - private def boolean isContainedInOtherThanOwnResource(EObject eObject, Iterable resources) { + private static def boolean isContainedInOtherThanOwnResource(EObject eObject, Iterable resources) { val rootContainerResource = EcoreUtil.getRootContainer(eObject).eResource return rootContainerResource !== eObject.eResource && resources.contains(rootContainerResource) } @@ -189,7 +176,7 @@ class ResourceCopier { * Copies the IDs of the source's elements to the target. * It is expected that both resources are in an identical state. */ - private def void copyUmlIds(XMLResource source, XMLResource target) { + private static def void copyUmlIds(XMLResource source, XMLResource target) { val sourceIterator = source.allContents val targetIterator = target.allContents while (sourceIterator.hasNext && targetIterator.hasNext) { @@ -202,23 +189,4 @@ class ResourceCopier { checkState(!sourceIterator.hasNext, "source uml resource has too many elements") checkState(!targetIterator.hasNext, "target uml resource has too many elements") } - - private def void registerInMapping(EObject original, EcoreUtil.Copier copier) { - copyToOriginalMapping.put(copier.get(original), original) - original.eContents.forEach [ registerInMapping(it, copier) ] - } - - private def void registerInMappingForUmlResources(Resource originalResource, Resource copiedResource) { - val originalIterator = originalResource.allContents - val copyIterator = copiedResource.allContents - while (originalIterator.hasNext && copyIterator.hasNext) { - val original = originalIterator.next - val copy = copyIterator.next - checkState(original.eClass === copy.eClass, "non matching elements %s and %s", original, - copy) - copyToOriginalMapping.put(copy, original) - } - checkState(!originalIterator.hasNext, "original uml resource has too many elements") - checkState(!copyIterator.hasNext, "copied uml resource has too many elements") - } } diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java index 770936502c..f34d8a4c5d 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java @@ -22,7 +22,7 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; -import tools.vitruv.change.atomic.EChangeIdManager; +import tools.vitruv.change.atomic.EChangeUuidManager; import tools.vitruv.change.atomic.uuid.UuidResolver; import tools.vitruv.change.composite.description.TransactionalChange; import tools.vitruv.change.composite.description.VitruviusChange; @@ -179,7 +179,7 @@ public Iterable recordChanges(Runnable changeApplicator) { isRecording = false; changeRecorder.endRecording(); TransactionalChange change = changeRecorder.getChange(); - EChangeIdManager.setOrGenerateIds(change.getEChanges(), uuidResolver); + EChangeUuidManager.setOrGenerateIds(change.getEChanges(), uuidResolver); return change.containsConcreteChange() ? List.of(change) : List.of(); } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend index d13b2c067a..72de3cdc17 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/BasicStateChangePropagationTest.xtend @@ -9,12 +9,13 @@ import org.junit.jupiter.params.provider.MethodSource import tools.vitruv.change.atomic.eobject.CreateEObject import tools.vitruv.change.atomic.eobject.DeleteEObject import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute +import tools.vitruv.change.atomic.id.IdResolver import tools.vitruv.change.atomic.root.InsertRootEObject import tools.vitruv.change.atomic.root.RemoveRootEObject import tools.vitruv.testutils.Capture -import static org.hamcrest.MatcherAssert.assertThat import static org.hamcrest.CoreMatchers.instanceOf +import static org.hamcrest.MatcherAssert.assertThat import static org.junit.jupiter.api.Assertions.assertEquals import static org.junit.jupiter.api.Assertions.assertTrue import static tools.vitruv.testutils.matchers.ModelMatchers.containsModelOf @@ -46,7 +47,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { // Create empty resource to apply generated changes to val validationResourceSet = new ResourceSetImpl() - changes.unresolve().resolveAndApply(validationResourceSet) + changes.resolveAndApply(IdResolver.create(validationResourceSet)) modelResource.save(null) assertEquals(1, validationResourceSet.resources.size) @@ -74,7 +75,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { // Load resource to apply generated changes to val validationResourceSet = new ResourceSetImpl() validationResourceSet.getResource(testUri, true) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.resolveAndApply(IdResolver.create(validationResourceSet)) assertEquals(1, validationResourceSet.resources.size) assertTrue(validationResourceSet.resources.get(0).contents.empty) @@ -105,7 +106,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val oldState = validationResourceSet.getResource(testUri, true) val changes = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.resolveAndApply(IdResolver.create(validationResourceSet)) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -136,7 +137,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { assertEquals(1, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.resolveAndApply(IdResolver.create(validationResourceSet)) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -164,7 +165,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationResourceSet = new ResourceSetImpl().withGlobalFactories() val oldState = validationResourceSet.getResource(testUri, true) val unresolvedChanges = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - val changes = unresolvedChanges.resolveAndApply(validationResourceSet) + val changes = unresolvedChanges.resolveAndApply(IdResolver.create(validationResourceSet)) switch (strategyToTest.useIdentifiers) { case ONLY, case WHEN_AVAILABLE: { @@ -184,8 +185,6 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { } } - changes.unresolve().resolveAndApply(validationResourceSet) - assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) } @@ -220,7 +219,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { assertEquals(1, changes.EChanges.size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.resolveAndApply(IdResolver.create(validationResourceSet)) assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) @@ -253,7 +252,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { val validationResourceSet = new ResourceSetImpl().withGlobalFactories() val oldState = validationResourceSet.getResource(testUri, true) val unresolvedChanges = strategyToTest.getChangeSequenceBetween(-modelResource, oldState) - val changes = unresolvedChanges.resolveAndApply(validationResourceSet) + val changes = unresolvedChanges.resolveAndApply(IdResolver.create(validationResourceSet)) switch (strategyToTest.useIdentifiers) { case ONLY, case WHEN_AVAILABLE: { @@ -273,8 +272,6 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { } } - changes.unresolve().resolveAndApply(validationResourceSet) - assertEquals(1, validationResourceSet.resources.size) assertThat(validationResourceSet.resources.get(0), containsModelOf(-modelResource)) } @@ -309,7 +306,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { assertEquals(1, changes.EChanges.filter(RemoveRootEObject).size) assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.resolveAndApply(IdResolver.create(validationResourceSet)) (-modelResource).save(null) assertEquals(2, validationResourceSet.resources.size) @@ -349,7 +346,7 @@ class BasicStateChangePropagationTest extends StateChangePropagationTest { assertEquals(1, changes.EChanges.filter(InsertRootEObject).size) assertEquals(1, changes.EChanges.filter(ReplaceSingleValuedEAttribute).size) - changes.unresolve().resolveAndApply(validationResourceSet) + changes.resolveAndApply(IdResolver.create(validationResourceSet)) (-modelResource).save(null) assertEquals(2, validationResourceSet.resources.size) diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend index 7add7659f2..a03126e8f9 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/StateChangePropagationTest.xtend @@ -15,6 +15,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Named import org.junit.jupiter.api.^extension.ExtendWith import pcm_mockup.Repository +import tools.vitruv.change.atomic.id.IdResolver import tools.vitruv.change.composite.description.VitruviusChange import tools.vitruv.change.composite.recording.ChangeRecorder import tools.vitruv.testutils.RegisterMetamodelsInStandalone @@ -99,7 +100,7 @@ abstract class StateChangePropagationTest { val deltaBasedChange = resourceSet.endRecording val unresolvedStateBasedChange = strategyToTest.getChangeSequenceBetween(model, checkpoint) assertNotNull(unresolvedStateBasedChange) - val stateBasedChange = unresolvedStateBasedChange.resolveAndApply(checkpoint.resourceSet) + val stateBasedChange = unresolvedStateBasedChange.resolveAndApply(IdResolver.create(checkpoint.resourceSet)) val message = getTextualRepresentation(stateBasedChange, deltaBasedChange) val stateBasedChangedObjects = stateBasedChange.affectedAndReferencedEObjects val deltaBasedChangedObjects = deltaBasedChange.affectedAndReferencedEObjects diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java index e1ca931372..cda335aa31 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/BasicViewTest.java @@ -143,7 +143,7 @@ public void withoutPreviousModification() throws Exception { @DisplayName("with previous modification") public void withPreviousModification() throws Exception { try (BasicView view = new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection)) { - view.modifyContents((resourceSet, uuidResolver) -> resourceSet.createResource(URI.createURI("test://test.aet"))); + view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); assertThrows(IllegalStateException.class, () -> view.update()); } } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java index 76626be254..d51d20b8d0 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeDerivingViewTest.java @@ -31,6 +31,7 @@ import allElementTypes.Root; import tools.vitruv.change.atomic.eobject.EobjectPackage; import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; +import tools.vitruv.change.atomic.id.IdResolver; import tools.vitruv.change.atomic.root.InsertRootEObject; import tools.vitruv.change.atomic.root.RootFactory; import tools.vitruv.change.atomic.root.RootPackage; @@ -210,7 +211,7 @@ public void commitChanges() throws Exception { view.commitChanges(); verify(mockViewType).commitViewChanges(viewArgument.capture(), changeArgument.capture()); assertThat(viewArgument.getValue(), is(view)); - VitruviusChange change = changeArgument.getValue().resolveAndApply(root.eResource().getResourceSet()); + VitruviusChange change = changeArgument.getValue().resolveAndApply(IdResolver.create(root.eResource().getResourceSet())); InsertRootEObject expectedChange = RootFactory.eINSTANCE.createInsertRootEObject(); expectedChange.setNewValue(root); expectedChange.setUri(testResourceUriString); diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java index 521ccc140a..01bb50a503 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/ChangeRecordingViewTest.java @@ -149,7 +149,7 @@ public void withoutPreviousModification() throws Exception { @DisplayName("with previous modification") public void withPreviousModification() throws Exception { try (ChangeRecordingView view = new ChangeRecordingView(new BasicView(mockViewType, mockChangeableViewSource, mockViewSelection))) { - view.modifyContents((resourceSet, uuidResolver) -> resourceSet.createResource(URI.createURI("test://test.aet"))); + view.modifyContents((resourceSet) -> resourceSet.createResource(URI.createURI("test://test.aet"))); assertThrows(IllegalStateException.class, () -> view.update()); } } diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java index aa87fc1c44..ce2cf79b28 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/util/XmiIdEdgeCaseTest.java @@ -45,7 +45,7 @@ void setup(@TestProject Path testProjectFolder) { public void testSingleResourceCopy() { Pair> umlResourcePair = createPopulatedUmlResourceAndIdMapping("my"); ResourceSet copyResourceSet = new ResourceSetImpl(); - XMLResource copiedModel = (XMLResource) new ResourceCopier().copyViewResource(umlResourcePair.get0(), + XMLResource copiedModel = (XMLResource) ResourceCopier.copyViewResource(umlResourcePair.get0(), copyResourceSet); validateIds(copiedModel, umlResourcePair.get1()); } @@ -57,7 +57,7 @@ public void testMultiResourceCopy() { Pair> umlResourcePair3 = createPopulatedUmlResourceAndIdMapping("my3"); ResourceSet copyResourceSet = new ResourceSetImpl(); - Map copiedModels = new ResourceCopier().copyViewResources( + Map copiedModels = ResourceCopier.copyViewResources( List.of(umlResourcePair.get0(), umlResourcePair2.get0(), umlResourcePair3.get0()), copyResourceSet); XMLResource copiedModel = (XMLResource) copiedModels.get(umlResourcePair.get0()); XMLResource copiedModel2 = (XMLResource) copiedModels.get(umlResourcePair2.get0()); diff --git a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend index dc4079bf7d..d505b0b25c 100644 --- a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend +++ b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTest.xtend @@ -9,7 +9,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.^extension.ExtendWith -import tools.vitruv.change.atomic.EChangeIdManager +import tools.vitruv.change.atomic.EChangeUuidManager import tools.vitruv.change.atomic.eobject.CreateEObject import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute import tools.vitruv.change.atomic.feature.reference.ReplaceSingleValuedEReference @@ -372,7 +372,7 @@ class VirtualModelTest { private def endRecording(ChangeRecorder changeRecorder, UuidResolver uuidResolver) { val change = changeRecorder.endRecording - EChangeIdManager.setOrGenerateIds(change.EChanges, uuidResolver) + EChangeUuidManager.setOrGenerateIds(change.EChanges, uuidResolver) return change } diff --git a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend index c862b30761..3686b162fb 100644 --- a/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend +++ b/tests/tools.vitruv.framework.vsum.tests/src/tools/vitruv/framework/vsum/VirtualModelTestUtil.xtend @@ -7,7 +7,7 @@ import java.nio.file.Path import org.eclipse.emf.common.util.URI import org.eclipse.emf.ecore.resource.ResourceSet import tools.vitruv.change.atomic.EChange -import tools.vitruv.change.atomic.EChangeIdManager +import tools.vitruv.change.atomic.EChangeUuidManager import tools.vitruv.change.atomic.root.InsertRootEObject import tools.vitruv.change.atomic.uuid.UuidResolver import tools.vitruv.change.composite.MetamodelDescriptor @@ -37,7 +37,7 @@ class VirtualModelTestUtil { recorder.beginRecording changesToPerform.run() val result = recorder.endRecording - EChangeIdManager.setOrGenerateIds(result.EChanges, uuidResolver) + EChangeUuidManager.setOrGenerateIds(result.EChanges, uuidResolver) recorder.close return result } From b26613938ec4f42bfcda255d12d2dfe3ce2ef737 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Sun, 5 Feb 2023 13:30:40 +0100 Subject: [PATCH 16/19] fix identity mapping view type test --- .../impl/IdentityMappingViewTypeTest.java | 56 ++++++++++++++++--- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java index ccdde44d29..1bd50cc174 100644 --- a/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java +++ b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/impl/IdentityMappingViewTypeTest.java @@ -5,14 +5,18 @@ import static org.hamcrest.CoreMatchers.anything; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static tools.vitruv.change.atomic.resolve.EChangeIdResolverAndApplicator.applyBackward; +import static tools.vitruv.change.atomic.resolve.EChangeIdResolverAndApplicator.applyForward; import static tools.vitruv.testutils.matchers.ModelMatchers.equalsDeeply; import static tools.vitruv.testutils.metamodels.AllElementTypesCreators.aet; @@ -21,6 +25,7 @@ import java.util.stream.Stream; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; @@ -38,11 +43,16 @@ import allElementTypes.Root; import tools.vitruv.change.atomic.EChange; +import tools.vitruv.change.atomic.EChangeIdManager; +import tools.vitruv.change.atomic.feature.attribute.ReplaceSingleValuedEAttribute; +import tools.vitruv.change.atomic.id.IdResolver; +import tools.vitruv.change.atomic.uuid.UuidResolver; import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.change.composite.description.VitruviusChangeFactory; import tools.vitruv.change.composite.recording.ChangeRecorder; import tools.vitruv.framework.views.ChangeableViewSource; import tools.vitruv.framework.views.View; +import tools.vitruv.framework.views.ViewSelection; import tools.vitruv.framework.views.ViewType; import tools.vitruv.framework.views.selectors.DirectViewElementSelector; import tools.vitruv.testutils.RegisterMetamodelsInStandalone; @@ -355,26 +365,40 @@ public void removingUnselectedRoot() throws Exception { @DisplayName("commit view changes") class CommitViewChanges { private IdentityMappingViewType basicViewType; - private ResourceSet testResourceSet; + private ResourceSet viewSourceResourceSet; + private ResourceSet viewResourceSet; private ModifiableView view; + private ViewSelection viewSelection; private ChangeableViewSource viewSource; + private UuidResolver uuidResolver; @BeforeEach void initializeViewTypeAndResourceSetAndViewSource() { this.basicViewType = new IdentityMappingViewType("name"); - this.testResourceSet = withGlobalFactories(new ResourceSetImpl()); + this.viewSourceResourceSet = withGlobalFactories(new ResourceSetImpl()); + this.viewResourceSet = withGlobalFactories(new ResourceSetImpl()); this.view = mock(ModifiableView.class); this.viewSource = mock(ChangeableViewSource.class); + this.viewSelection = mock(ViewSelection.class); + this.uuidResolver = UuidResolver.create(viewSourceResourceSet); when(view.getViewSource()).thenReturn(viewSource); - when(viewSource.getViewSourceModels()).thenReturn(testResourceSet.getResources()); + when(view.getSelection()).thenReturn(viewSelection); + when(viewSource.getViewSourceModels()).thenReturn(viewSourceResourceSet.getResources()); + when(viewSource.getUuidResolver()).thenReturn(uuidResolver); + when(viewSelection.isViewObjectSelected(any(EObject.class))).thenReturn(true); } private Root createResourceWithSingleRoot(URI uri) { - Resource resource = testResourceSet.createResource(uri); + Resource resource = viewSourceResourceSet.createResource(uri); Root rootElement = aet.Root(); + uuidResolver.registerEObject(rootElement); rootElement.setId("testid"); resource.getContents().add(rootElement); - return rootElement; + + Resource viewResource = viewResourceSet.createResource(uri); + Root viewRootElement = EcoreUtil.copy(rootElement); + viewResource.getContents().add(viewRootElement); + return viewRootElement; } @Test @@ -412,20 +436,36 @@ private static Stream testEmptyChanges() { @DisplayName("with non-empty change") void withNonEmptyChange() { Root root = createResourceWithSingleRoot(URI.createURI("test://test.aet")); - try (ChangeRecorder changeRecorder = new ChangeRecorder(testResourceSet)) { + try (ChangeRecorder changeRecorder = new ChangeRecorder(viewResourceSet)) { changeRecorder.addToRecording(root); changeRecorder.beginRecording(); root.setId("testid2"); changeRecorder.endRecording(); - VitruviusChange change = changeRecorder.getChange().unresolve(); + VitruviusChange change = changeRecorder.getChange(); + assignIds(change); ArgumentCaptor changeArgument = ArgumentCaptor.forClass(VitruviusChange.class); basicViewType.commitViewChanges(view, change); verify(viewSource).propagateChange(changeArgument.capture()); List eChanges = changeArgument.getValue().unresolve().getEChanges(); assertThat(eChanges.size(), is(1)); - assertThat(eChanges.get(0), equalsDeeply(change.getEChanges().get(0))); + assertThat(eChanges.get(0), instanceOf(ReplaceSingleValuedEAttribute.class)); + var attributeChange = (ReplaceSingleValuedEAttribute)eChanges.get(0); + assertThat(attributeChange.getOldValue(), is("testid")); + assertThat(attributeChange.getNewValue(), is("testid2")); + } + } + + private void assignIds(VitruviusChange change) { + IdResolver idResolver = IdResolver.create(viewResourceSet); + EChangeIdManager idManager = new EChangeIdManager(idResolver); + for (int i = change.getEChanges().size() - 1; i >= 0; i--) { + applyBackward(change.getEChanges().get(i)); } + change.getEChanges().forEach(eChange -> { + idManager.setOrGenerateIds(eChange); + applyForward(eChange, idResolver); + }); } } } From f533017e292a353b1f7d844aab067666845e56fb Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Mon, 6 Feb 2023 09:24:25 +0100 Subject: [PATCH 17/19] remove outdated TODO comment --- .../src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend | 1 - 1 file changed, 1 deletion(-) diff --git a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend index b1c57bcb3e..a8eac4e23c 100644 --- a/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend +++ b/bundles/tools.vitruv.testutils.vsum/src/tools/vitruv/testutils/LegacyVitruvApplicationTest.xtend @@ -51,7 +51,6 @@ abstract class LegacyVitruvApplicationTest extends VitruvApplicationTest impleme } private def dispatch EObject resolveInVirtualModel(EObject object) { - // TODO: JW as soon as this class becomes not needed anymore, we should hide the tools.vitruv.change.atomic.id package in tools.vitruv.change.atomic (of Vitruv-Change repository). if (object.eResource !== null) { internalVirtualModel.getModelInstance(object.eResource.URI).resource.getEObject( object.hierarchicUriFragment) From 9e1fb302bcde6263abb4af5a210a24a2aed91fb3 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Mon, 6 Mar 2023 18:02:30 +0100 Subject: [PATCH 18/19] implement code suggestions --- .../views/impl/IdentityMappingViewType.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java index 1d489e94df..aeff1c8e7b 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java @@ -6,7 +6,8 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.function.Function; +import java.util.stream.Stream; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; @@ -40,10 +41,10 @@ public DirectViewElementSelector createSelector(ChangeableViewSource viewSource) if (!resource.getContents().isEmpty() && ResourceCopier.requiresFullCopy(resource)) { // We can only copy writable UML resources as a whole, so no option to select // specific root elements - return List.of(resource.getContents().get(0)); + return Stream.of(resource.getContents().get(0)); } - return resource.getContents(); - }).flatMap(List::stream).filter(it -> it != null).collect(Collectors.toList())); + return resource.getContents().stream(); + }).flatMap(Function.identity()).filter(it -> it != null).toList()); } @Override @@ -78,8 +79,7 @@ private Map createViewResources(ModifiableView view, Resourc Collection viewSources = view.getViewSource().getViewSourceModels(); ViewSelection selection = view.getSelection(); List resourcesWithSelectedElements = viewSources.stream() - .filter(resource -> resource.getContents().stream().anyMatch(selection::isViewObjectSelected)) - .collect(Collectors.toList()); + .filter(resource -> resource.getContents().stream().anyMatch(selection::isViewObjectSelected)).toList(); return ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, viewResourceSet, selection::isViewObjectSelected); } From d0e050d784266865d514aa556e18543d873d66ed Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Mon, 3 Apr 2023 12:39:09 +0200 Subject: [PATCH 19/19] implement code suggestions --- .../vitruv/framework/views/impl/IdentityMappingViewType.java | 2 +- .../vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java index aeff1c8e7b..21af16be71 100644 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java @@ -39,7 +39,7 @@ public DirectViewElementSelector createSelector(ChangeableViewSource viewSource) return new DirectViewElementSelector(this, viewSource, viewSource.getViewSourceModels().stream().map(resource -> { if (!resource.getContents().isEmpty() && ResourceCopier.requiresFullCopy(resource)) { - // We can only copy writable UML resources as a whole, so no option to select + // Some resources (like UML) can only be copied as a whole, so no option to select // specific root elements return Stream.of(resource.getContents().get(0)); } diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend index 7752ff3bf4..06f682af3f 100644 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/helper/VsumFileSystemLayout.xtend @@ -13,7 +13,7 @@ import org.eclipse.emf.common.util.URI class VsumFileSystemLayout { static final String CORRESPONDENCES_FILE = "correspondences.correspondence"; - static final String UUIDS_FILE = "Uuid.uuid"; + static final String UUIDS_FILE = "uuid.uuid"; static final String MODELS_FILE = "models.models"; static final String VSUM_FOLDER_NAME = "vsum"; static final String CONSISTENCY_METADATA_FOLDER_NAME = "consistencymetadata";