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 69675d93e2..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 @@ -10,19 +10,24 @@ 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.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.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 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 * diff to a sequence of individual changes. - * @author Timur Saglam */ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResolutionStrategy { /** The identifier matching behavior used by this strategy */ @@ -93,9 +98,22 @@ class DefaultStateBasedChangeResolutionStrategy implements StateBasedChangeResol 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. 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..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,8 +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 package class BasicView implements ModifiableView, ChangePropagationListener { @Accessors(PUBLIC_GETTER, PROTECTED_SETTER) @@ -29,7 +29,7 @@ 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 boolean modelChanged @Accessors(PROTECTED_SETTER) 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 new file mode 100644 index 0000000000..21af16be71 --- /dev/null +++ b/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.java @@ -0,0 +1,86 @@ +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; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +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}. + */ +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)) { + // 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)); + } + return resource.getContents().stream(); + }).flatMap(Function.identity()).filter(it -> it != null).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) -> { + viewResourceSet.getResources().forEach(Resource::unload); + viewResourceSet.getResources().clear(); + 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)).toList(); + return ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, viewResourceSet, + selection::isViewObjectSelected); + } +} 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 cdc675c22d..0000000000 --- a/bundles/tools.vitruv.framework.views/src/tools/vitruv/framework/views/impl/IdentityMappingViewType.xtend +++ /dev/null @@ -1,58 +0,0 @@ -package tools.vitruv.framework.views.impl - -import tools.vitruv.change.composite.description.VitruviusChange -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 com.google.common.base.Preconditions.checkNotNull - -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 | - viewResourceSet.resources.forEach[unload] - viewResourceSet.resources.clear - val viewSources = view.viewSource.viewSourceModels - val selection = view.selection - val resourcesWithSelectedElements = viewSources.filter[contents.exists[selection.isViewObjectSelected(it)]] - ResourceCopier.copyViewSourceResources(resourcesWithSelectedElements, viewResourceSet) [selection.isViewObjectSelected(it)] - ] - } - - override commitViewChanges(ModifiableView view, VitruviusChange viewChange) { - checkNotNull(view, "view must not be null") - checkNotNull(viewChange, "view change must not be null"); - view.viewSource.propagateChange(viewChange) - } - -} 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..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,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..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,7 +1,7 @@ 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 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..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 @@ -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; @@ -11,11 +10,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; @@ -24,6 +22,8 @@ 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.uuid.UuidResolver; import tools.vitruv.change.composite.description.TransactionalChange; import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.change.composite.recording.ChangeRecorder; @@ -39,47 +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); 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) { - 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); - fileExtensionsToRecorder.put(fileExtensionsSet, recorder); - } - } - ResourceRepositoryImpl(VsumFileSystemLayout fileSystemLayout) { this.fileSystemLayout = fileSystemLayout; this.correspondenceModel = createPersistableCorrespondenceModel(fileSystemLayout.getCorrespondencesURI()); @@ -100,20 +68,23 @@ 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 { - for (String modelPath : Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath())) { - URI uri = URI.createURI(modelPath); - loadOrCreateResource(modelsResourceSet, uri); - createOrLoadModel(uri); - } + modelUris = Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath()).stream().map(URI::createURI) + .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 @@ -126,6 +97,11 @@ public ModelInstance getModel(URI modelUri) { return modelInstances.get(modelUri); } + @Override + public UuidResolver getUuidResolver() { + return uuidResolver; + } + private ModelInstance getCreateOrLoadModelUnlessLoading(URI modelUri) { if (isLoading) { return null; @@ -157,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); - } - 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(); } } } @@ -192,6 +163,7 @@ public void saveOrDeleteModels() { correspondenceModel.save(); try { writeModelsFile(); + uuidResolver.storeAtUri(fileSystemLayout.getUuidsURI()); } catch (IOException e) { throw new IllegalStateException(e); } @@ -199,27 +171,28 @@ 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(); + EChangeUuidManager.setOrGenerateIds(change.getEChanges(), uuidResolver); + return change.containsConcreteChange() ? List.of(change) : List.of(); } @Override public VitruviusChange applyChange(VitruviusChange change) { - return change.resolveAndApply(modelsResourceSet); + return change.resolveAndApply(uuidResolver); } - + @Override public URI getMetadataModelURI(String... metadataKey) { return fileSystemLayout.getConsistencyMetadataModelURI(metadataKey); } - + @Override public Resource getModelResource(URI uri) { return getCreateOrLoadModel(uri).getResource(); @@ -232,8 +205,9 @@ public Collection getModelResources() { @Override public void close() { - fileExtensionRecorderMapping.fileExtensionsToRecorder.values().forEach(ChangeRecorder::close); + changeRecorder.close(); modelsResourceSet.getResources().forEach(Resource::unload); modelsResourceSet.getResources().clear(); + uuidResolver.endTransaction(); } } 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/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..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 @@ -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() { 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..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 @@ -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. *

@@ -24,7 +25,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 +41,7 @@ abstract class LegacyVitruvApplicationTest extends VitruvApplicationTest impleme return internalVirtualModel.correspondenceModel.getCorrespondingEObjects(resolvedObject, tag).filter(type) } } - + private def getInternalVirtualModel() { return virtualModel as InternalVirtualModel } @@ -50,7 +52,8 @@ abstract class LegacyVitruvApplicationTest extends VitruvApplicationTest impleme private def dispatch EObject resolveInVirtualModel(EObject object) { if (object.eResource !== null) { - internalVirtualModel.getModelInstance(object.eResource.URI).resource.getEObject(object.hierarchicUriFragment) + internalVirtualModel.getModelInstance(object.eResource.URI).resource.getEObject( + object.hierarchicUriFragment) } } 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/EdgeCaseStateChangeTest.xtend b/tests/tools.vitruv.framework.views.tests/src/tools/vitruv/framework/views/changederivation/EdgeCaseStateChangeTest.xtend index f3a16d8788..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 @@ -33,7 +33,9 @@ class EdgeCaseStateChangeTest extends StateChangePropagationTest { @MethodSource("strategiesToTest") def void testNullResources(StateBasedChangeResolutionStrategy strategyToTest) { val Resource nullResource = null + assertThrows(IllegalArgumentException)[strategyToTest.getChangeSequenceForCreated(nullResource)] 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 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/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/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); + }); } } } 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 7182413b53..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,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.EChangeUuidManager 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 @@ -51,6 +53,7 @@ class VirtualModelTest { def void propagateIntoVirtualModel() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording @@ -59,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)) @@ -70,6 +73,7 @@ class VirtualModelTest { def void propagateIntoVirtualModelWithConsistency() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording @@ -78,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( @@ -93,6 +97,7 @@ class VirtualModelTest { def void singleChangeForRootElementInMultipleResource() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording @@ -108,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) @@ -123,6 +128,7 @@ class VirtualModelTest { def void singleChangeForElementContainedInRootElementInMultipleResource() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording @@ -145,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) @@ -160,6 +166,7 @@ class VirtualModelTest { def void savedVirtualModel() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording @@ -168,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) @@ -180,6 +187,7 @@ class VirtualModelTest { def void reloadVirtualModel() { val virtualModel = createAndLoadTestVirtualModel(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording @@ -189,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) @@ -199,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) } @@ -209,6 +217,7 @@ class VirtualModelTest { def void reloadVirtualModelWithConsistency() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording @@ -218,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) @@ -238,6 +247,7 @@ class VirtualModelTest { def void moveCorrespondingToOtherResourceAndBack() { val virtualModel = createAndLoadTestVirtualModelWithConsistencyPreservation(pathToVirtualModelProjectFolder) val resourceSet = new ResourceSetImpl().withGlobalFactories + val uuidResolver = UuidResolver.create(resourceSet) val changeRecorder = new ChangeRecorder(resourceSet) changeRecorder.addToRecording(resourceSet) changeRecorder.beginRecording @@ -248,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( @@ -263,7 +273,7 @@ class VirtualModelTest { monitoredResource => [ contents += root ] - virtualModel.propagateChange(changeRecorder.endRecording) + virtualModel.propagateChange(changeRecorder.endRecording(uuidResolver)) assertNull(virtualModel.getModelInstance(testIntermediateUri)) assertNull( virtualModel.getModelInstance( @@ -275,7 +285,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 +301,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 +333,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: @@ -356,9 +369,15 @@ class VirtualModelTest { private def getPathToVirtualModelProjectFolder() { projectFolder.resolve("vsum") } + + private def endRecording(ChangeRecorder changeRecorder, UuidResolver uuidResolver) { + val change = changeRecorder.endRecording + EChangeUuidManager.setOrGenerateIds(change.EChanges, uuidResolver) + return change + } - 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 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..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 @@ -1,27 +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.EChangeUuidManager 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 - /** * Utility methods for the VSUM and view test cases. */ @@ -31,12 +31,13 @@ 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) { + def static VitruviusChange recordChanges(ResourceSet resourceSet, UuidResolver uuidResolver, Runnable changesToPerform) { val recorder = new ChangeRecorder(resourceSet) recorder.addToRecording(resourceSet) recorder.beginRecording changesToPerform.run() val result = recorder.endRecording + EChangeUuidManager.setOrGenerateIds(result.EChanges, uuidResolver) recorder.close return result }