From 8762ed839dbcb737cd8b2efc76f7977fc2e8d654 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Thu, 20 Oct 2022 11:25:40 +0200 Subject: [PATCH 1/3] rewrite VirtualModelImpl in Java --- .../vsum/internal/VirtualModelImpl.java | 171 ++++++++++++++++++ .../vsum/internal/VirtualModelImpl.xtend | 152 ---------------- 2 files changed, 171 insertions(+), 152 deletions(-) create mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.xtend 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 new file mode 100644 index 0000000000..7fd8a11f13 --- /dev/null +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.java @@ -0,0 +1,171 @@ +package tools.vitruv.framework.vsum.internal; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; + +import tools.vitruv.change.composite.description.PropagatedChange; +import tools.vitruv.change.composite.description.VitruviusChange; +import tools.vitruv.change.composite.propagation.ChangePropagationListener; +import tools.vitruv.change.correspondence.Correspondence; +import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView; +import tools.vitruv.change.interaction.InternalUserInteractor; +import tools.vitruv.change.propagation.ChangePropagationMode; +import tools.vitruv.change.propagation.ChangePropagationSpecificationProvider; +import tools.vitruv.change.propagation.impl.ChangePropagator; +import tools.vitruv.framework.views.ViewSelector; +import tools.vitruv.framework.views.ViewType; +import tools.vitruv.framework.views.ViewTypeProvider; +import tools.vitruv.framework.views.ViewTypeRepository; +import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout; + +public class VirtualModelImpl implements InternalVirtualModel { + private static final Logger LOGGER = Logger.getLogger(VirtualModelImpl.class); + + private final ModelRepository resourceRepository; + private final ViewTypeProvider viewTypeRepository; + private final VsumFileSystemLayout fileSystemLayout; + private final List changePropagationListeners = new LinkedList<>(); + private final ChangePropagator changePropagator; + + public VirtualModelImpl(VsumFileSystemLayout fileSystemLayout, InternalUserInteractor userInteractor, + ViewTypeRepository viewTypeRepository, + ChangePropagationSpecificationProvider changePropagationSpecificationProvider) { + this.fileSystemLayout = fileSystemLayout; + this.viewTypeRepository = viewTypeRepository; + resourceRepository = new ResourceRepositoryImpl(fileSystemLayout); + changePropagator = new ChangePropagator(resourceRepository, changePropagationSpecificationProvider, + userInteractor); + } + + public void loadExistingModels() { + resourceRepository.loadExistingModels(); + } + + @Override + public synchronized EditableCorrespondenceModelView getCorrespondenceModel() { + return resourceRepository.getCorrespondenceModel(); + } + + @Override + public synchronized ModelInstance getModelInstance(URI modelUri) { + return resourceRepository.getModel(modelUri); + } + + private synchronized void save() { + resourceRepository.saveOrDeleteModels(); + } + + @Override + public synchronized List propagateChange(VitruviusChange change) { + checkNotNull(change, "change to propagate"); + checkArgument(change.containsConcreteChange(), "This change contains no concrete change:%s%s", + System.lineSeparator(), change); + VitruviusChange unresolvedChange = change.unresolve(); + + LOGGER.info("Starting change propagation"); + startChangePropagation(unresolvedChange); + + List result = changePropagator.propagateChange(unresolvedChange); + save(); + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Propagated changes: " + result); + } + + finishChangePropagation(unresolvedChange, result); + LOGGER.info("Finished change propagation"); + return result; + } + + private void startChangePropagation(VitruviusChange change) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Started synchronizing change: " + change); + } + changePropagationListeners.stream().forEach(it -> it.startedChangePropagation(change)); + } + + private void finishChangePropagation(VitruviusChange inputChange, Iterable generatedChanges) { + changePropagationListeners.stream().forEach(it -> it.finishedChangePropagation(generatedChanges)); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Finished synchronizing change: " + inputChange); + } + } + + @Override + public Path getFolder() { + return fileSystemLayout.getVsumProjectFolder(); + } + + /** + * Registers the given {@link ChangePropagationListener}. The listener must not + * be null. + */ + @Override + public synchronized void addChangePropagationListener(ChangePropagationListener propagationListener) { + this.changePropagationListeners.add(checkNotNull(propagationListener, "propagationListener")); + } + + /** + * Unregisters the given {@link ChangePropagationListener}. The listener must + * not be null. + */ + @Override + public synchronized void removeChangePropagationListener(ChangePropagationListener propagationListener) { + this.changePropagationListeners.remove(checkNotNull(propagationListener, "propagationListener")); + } + + /** + * Returns the name of the virtual model. + * + * @return The name of the virtual model + */ + public String getName() { + return getFolder().getFileName().toString(); + } + + @Override + public void dispose() { + try { + resourceRepository.close(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + VirtualModelRegistry.getInstance().deregisterVirtualModel(this); + } + + @Override + public Collection getViewSourceModels() { + return resourceRepository.getModelResources(); + } + + @Override + public Collection> getViewTypes() { + return viewTypeRepository.getViewTypes(); + } + + @Override + public S createSelector(ViewType viewType) { + /* + * Note that ViewType.createSelector() accepts a ChangeableViewSource, which + * VirtualModelImpl implements but not its publicly used interface VitualModel. + * Thus calling viewType.createSelector(virtualModel) with virtualModel having + * the static type VirtualModel is not possible, i.e., this method hides + * implementation details and is not a convenience method. + */ + return viewType.createSelector(this); + } + + @Override + public void setChangePropagationMode(ChangePropagationMode changePropagationMode) { + changePropagator.setChangePropagationMode(changePropagationMode); + } +} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.xtend deleted file mode 100644 index 18ee163eef..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/VirtualModelImpl.xtend +++ /dev/null @@ -1,152 +0,0 @@ -package tools.vitruv.framework.vsum.internal - -import java.nio.file.Path -import java.util.LinkedList -import java.util.List -import org.apache.log4j.Logger -import org.eclipse.emf.common.util.URI -import org.eclipse.xtend.lib.annotations.Delegate -import tools.vitruv.change.composite.description.PropagatedChange -import tools.vitruv.change.composite.description.VitruviusChange -import tools.vitruv.change.composite.propagation.ChangePropagationListener -import tools.vitruv.change.propagation.ChangePropagationSpecificationProvider -import tools.vitruv.change.propagation.impl.ChangePropagator -import tools.vitruv.change.interaction.InternalUserInteractor -import tools.vitruv.framework.views.ViewSelector -import tools.vitruv.framework.views.ViewType -import tools.vitruv.framework.views.ViewTypeProvider -import tools.vitruv.framework.views.ViewTypeRepository -import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout - -import static com.google.common.base.Preconditions.checkArgument -import static com.google.common.base.Preconditions.checkNotNull -import tools.vitruv.change.propagation.ChangePropagationMode - -class VirtualModelImpl implements InternalVirtualModel { - static val Logger LOGGER = Logger.getLogger(VirtualModelImpl) - val ModelRepository resourceRepository - @Delegate val ViewTypeProvider viewTypeRepository - val ChangePropagator changePropagator - val VsumFileSystemLayout fileSystemLayout - val List changePropagationListeners = new LinkedList() - - new(VsumFileSystemLayout fileSystemLayout, InternalUserInteractor userInteractor, - ViewTypeRepository viewTypeRepository, - ChangePropagationSpecificationProvider changePropagationSpecificationProvider) { - this.fileSystemLayout = fileSystemLayout - this.viewTypeRepository = viewTypeRepository - resourceRepository = new ResourceRepositoryImpl(fileSystemLayout) - changePropagator = new ChangePropagator( - resourceRepository, - changePropagationSpecificationProvider, - userInteractor - ) - VirtualModelRegistry.instance.registerVirtualModel(this) - } - - def loadExistingModels() { - this.resourceRepository.loadExistingModels() - } - - override synchronized getCorrespondenceModel() { - this.resourceRepository.correspondenceModel - } - - override synchronized getModelInstance(URI modelUri) { - this.resourceRepository.getModel(modelUri) - } - - private def synchronized save() { - this.resourceRepository.saveOrDeleteModels() - } - - override synchronized propagateChange(VitruviusChange change) { - checkNotNull(change, "change to propagate") - checkArgument(change.containsConcreteChange, "This change contains no concrete changes:%s%s", - System.lineSeparator, change) - val unresolvedChange = change.unresolve() - - LOGGER.info("Start change propagation") - startChangePropagation(unresolvedChange) - - val result = changePropagator.propagateChange(unresolvedChange) - save() - - if (LOGGER.isTraceEnabled) { - LOGGER.trace(''' - Propagated changes: - «FOR propagatedChange : result» - Propagated Change: - «propagatedChange» - «ENDFOR» - ''') - } - - finishChangePropagation(unresolvedChange, result) - LOGGER.info("Finished change propagation") - return result - } - - private def void startChangePropagation(VitruviusChange change) { - if(LOGGER.isDebugEnabled) LOGGER.debug('''Started synchronizing change: «change»''') - changePropagationListeners.forEach[startedChangePropagation(change)] - } - - private def void finishChangePropagation(VitruviusChange inputChange, Iterable generatedChanges) { - changePropagationListeners.forEach[finishedChangePropagation(generatedChanges)] - if(LOGGER.isDebugEnabled) LOGGER.debug('''Finished synchronizing change: «inputChange»''') - } - - override Path getFolder() { - return fileSystemLayout.vsumProjectFolder - } - - /** - * Registers the given {@link ChangePropagationListener}. - * The listener must not be null. - */ - override synchronized void addChangePropagationListener(ChangePropagationListener propagationListener) { - this.changePropagationListeners.add(checkNotNull(propagationListener, "propagationListener")) - } - - /** - * Unregisters the given {@link ChangePropagationListener}. - * The listener must not be null. - */ - override synchronized void removeChangePropagationListener(ChangePropagationListener propagationListener) { - this.changePropagationListeners.remove(checkNotNull(propagationListener, "propagationListener")) - } - - /** - * Returns the name of the virtual model. - * - * @return The name of the virtual model - */ - def getName() { - folder.fileName.toString - } - - override void dispose() { - resourceRepository.close() - VirtualModelRegistry.instance.deregisterVirtualModel(this) - } - - override getViewSourceModels() { - resourceRepository.modelResources - } - - override createSelector(ViewType viewType) { - /* Note that ViewType.createSelector() accepts a ChangeableViewSource, which - * VirtualModelImpl implements but not its publicly used interface VitualModel. - * Thus calling viewType.createSelector(virtualModel) with virtualModel having - * the static type VirtualModel is not possible, i.e., this method hides - * implementation details and is not a convenience method. - */ - viewType.createSelector(this) - } - - override setChangePropagationMode(ChangePropagationMode changePropagationMode) { - changePropagator.changePropagationMode = changePropagationMode - } - -} From 8c27e2c8463f36f69ef6661d397a00b731c3a117 Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Thu, 20 Oct 2022 12:05:55 +0200 Subject: [PATCH 2/3] create change propagator only during change propagation instead of for entire V-SUM lifetime --- .../framework/vsum/internal/VirtualModelImpl.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 7fd8a11f13..372177ca91 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 @@ -34,7 +34,10 @@ public class VirtualModelImpl implements InternalVirtualModel { private final ViewTypeProvider viewTypeRepository; private final VsumFileSystemLayout fileSystemLayout; private final List changePropagationListeners = new LinkedList<>(); - private final ChangePropagator changePropagator; + + private final ChangePropagationSpecificationProvider changePropagationSpecificationProvider; + private final InternalUserInteractor userInteractor; + private ChangePropagationMode changePropagationMode = ChangePropagationMode.TRANSITIVE_CYCLIC; public VirtualModelImpl(VsumFileSystemLayout fileSystemLayout, InternalUserInteractor userInteractor, ViewTypeRepository viewTypeRepository, @@ -42,8 +45,9 @@ public VirtualModelImpl(VsumFileSystemLayout fileSystemLayout, InternalUserInter this.fileSystemLayout = fileSystemLayout; this.viewTypeRepository = viewTypeRepository; resourceRepository = new ResourceRepositoryImpl(fileSystemLayout); - changePropagator = new ChangePropagator(resourceRepository, changePropagationSpecificationProvider, - userInteractor); + + this.changePropagationSpecificationProvider = changePropagationSpecificationProvider; + this.userInteractor = userInteractor; } public void loadExistingModels() { @@ -74,6 +78,8 @@ public synchronized List propagateChange(VitruviusChange chang LOGGER.info("Starting change propagation"); startChangePropagation(unresolvedChange); + ChangePropagator changePropagator = new ChangePropagator(resourceRepository, + changePropagationSpecificationProvider, userInteractor, changePropagationMode); List result = changePropagator.propagateChange(unresolvedChange); save(); @@ -166,6 +172,6 @@ public S createSelector(ViewType viewType) { @Override public void setChangePropagationMode(ChangePropagationMode changePropagationMode) { - changePropagator.setChangePropagationMode(changePropagationMode); + this.changePropagationMode = changePropagationMode; } } From 0411173d0b30b649b3d583a31ed03452109691ce Mon Sep 17 00:00:00 2001 From: Jan Wittler Date: Wed, 26 Oct 2022 14:43:09 +0200 Subject: [PATCH 3/3] rewrite ResourceRepositoryImpl in Java --- .../vsum/internal/ResourceRepositoryImpl.java | 240 ++++++++++++++++++ .../internal/ResourceRepositoryImpl.xtend | 201 --------------- 2 files changed, 240 insertions(+), 201 deletions(-) create mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java delete mode 100644 bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.xtend 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 new file mode 100644 index 0000000000..251f0a6c8b --- /dev/null +++ b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.java @@ -0,0 +1,240 @@ +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; +import static tools.vitruv.change.correspondence.model.CorrespondenceModelFactory.createPersistableCorrespondenceModel; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.log4j.Logger; +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; + +import tools.vitruv.change.composite.description.TransactionalChange; +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.model.PersistableCorrespondenceModel; +import tools.vitruv.change.correspondence.view.CorrespondenceModelViewFactory; +import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView; +import tools.vitruv.change.propagation.impl.ResourceRegistrationAdapter; +import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout; + +class ResourceRepositoryImpl implements ModelRepository { + private static final Logger LOGGER = Logger.getLogger(ResourceRepositoryImpl.class); + + 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 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()); + modelsResourceSet.eAdapters() + .add(new ResourceRegistrationAdapter(resource -> getCreateOrLoadModelUnlessLoading(resource.getURI()))); + } + + @Override + public void loadExistingModels() { + isLoading = true; + try { + readModelsFile(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + correspondenceModel.loadSerializedCorrespondences(modelsResourceSet); + isLoading = false; + } + + private void writeModelsFile() throws IOException { + Files.write(fileSystemLayout.getModelsNamesFilesPath(), + modelsResourceSet.getResources().stream().map(Resource::getURI).map(URI::toString).collect(Collectors.toList())); + } + + private void readModelsFile() throws IOException { + try { + for (String modelPath : Files.readAllLines(fileSystemLayout.getModelsNamesFilesPath())) { + URI uri = URI.createURI(modelPath); + loadOrCreateResource(modelsResourceSet, uri); + createOrLoadModel(uri); + } + } catch (NoSuchFileException e) { + // There are no existing models, so don't do anything + } + } + + @Override + public EditableCorrespondenceModelView getCorrespondenceModel() { + return CorrespondenceModelViewFactory.createEditableCorrespondenceModelView(correspondenceModel); + } + + @Override + public ModelInstance getModel(URI modelUri) { + return modelInstances.get(modelUri); + } + + private ModelInstance getCreateOrLoadModelUnlessLoading(URI modelUri) { + if (isLoading) { + return null; + } + return getCreateOrLoadModel(modelUri); + } + + private ModelInstance getCreateOrLoadModel(URI modelUri) { + ModelInstance instance = getModel(modelUri); + if (instance != null) { + return instance; + } + return createOrLoadModel(modelUri); + } + + private ModelInstance createOrLoadModel(URI modelUri) { + Resource resource; + if (modelUri.isFile() || modelUri.isPlatform()) { + resource = getOrCreateResource(modelsResourceSet, modelUri); + } else { + resource = loadOrCreateResource(modelsResourceSet, modelUri); + } + ModelInstance modelInstance = new ModelInstance(resource); + modelInstances.put(modelUri, modelInstance); + registerRecorder(modelInstance); + return modelInstance; + } + + 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(); + } + } + } + + @Override + public void persistAsRoot(EObject rootObject, URI uri) { + getCreateOrLoadModel(uri).addRoot(rootObject); + } + + @Override + public void saveOrDeleteModels() { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Saving all models of model repository for VSUM " + fileSystemLayout); + } + Iterator> modelInstancesIterator = modelInstances.entrySet().iterator(); + while (modelInstancesIterator.hasNext()) { + ModelInstance modelInstance = modelInstancesIterator.next().getValue(); + if (modelInstance.isEmpty()) { + modelInstance.delete(); + modelInstancesIterator.remove(); + } else { + modelInstance.save(); + } + } + correspondenceModel.save(); + try { + writeModelsFile(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public Iterable recordChanges(Runnable changeApplicator) { + fileExtensionRecorderMapping.getRecorders().forEach(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).collect(Collectors.toList()); + } + + @Override + public VitruviusChange applyChange(VitruviusChange change) { + return change.resolveAndApply(modelsResourceSet); + } + + @Override + public URI getMetadataModelURI(String... metadataKey) { + return fileSystemLayout.getConsistencyMetadataModelURI(metadataKey); + } + + @Override + public Resource getModelResource(URI uri) { + return getCreateOrLoadModel(uri).getResource(); + } + + @Override + public Collection getModelResources() { + return modelsResourceSet.getResources(); + } + + @Override + public void close() { + fileExtensionRecorderMapping.fileExtensionsToRecorder.values().forEach(ChangeRecorder::close); + modelsResourceSet.getResources().forEach(Resource::unload); + modelsResourceSet.getResources().clear(); + } +} diff --git a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.xtend b/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.xtend deleted file mode 100644 index 40e187163e..0000000000 --- a/bundles/tools.vitruv.framework.vsum/src/tools/vitruv/framework/vsum/internal/ResourceRepositoryImpl.xtend +++ /dev/null @@ -1,201 +0,0 @@ -package tools.vitruv.framework.vsum.internal - -import java.util.HashMap -import java.util.Map -import org.apache.log4j.Logger -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.resource.Resource -import org.eclipse.emf.ecore.resource.ResourceSet - -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.loadOrCreateResource -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.getOrCreateResource -import static extension edu.kit.ipd.sdq.commons.util.org.eclipse.emf.ecore.resource.ResourceSetUtil.withGlobalFactories -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl -import tools.vitruv.framework.vsum.helper.VsumFileSystemLayout -import tools.vitruv.change.composite.recording.ChangeRecorder -import static tools.vitruv.change.correspondence.model.CorrespondenceModelFactory.createPersistableCorrespondenceModel -import org.eclipse.emf.common.util.URI -import java.nio.file.Files -import java.nio.file.NoSuchFileException -import tools.vitruv.change.composite.description.VitruviusChange -import static extension edu.kit.ipd.sdq.commons.util.java.lang.IterableUtil.mapFixed -import java.util.Set -import static com.google.common.base.Preconditions.checkState -import java.util.HashSet -import tools.vitruv.change.composite.description.TransactionalChange -import tools.vitruv.change.correspondence.model.PersistableCorrespondenceModel -import tools.vitruv.change.correspondence.view.CorrespondenceModelViewFactory -import tools.vitruv.change.propagation.impl.ResourceRegistrationAdapter - -package class ResourceRepositoryImpl implements ModelRepository { - static val logger = Logger.getLogger(ResourceRepositoryImpl) - val ResourceSet modelsResourceSet - val ResourceSet correspondencesResourceSet - val Map modelInstances = new HashMap() - val VsumFileSystemLayout fileSystemLayout - val PersistableCorrespondenceModel correspondenceModel - val extension FileExtensionRecorderMapping fileExtensionsRecorderMapping = new FileExtensionRecorderMapping - - var isRecording = false - var 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 { - val Map, ChangeRecorder> fileExtensionsToRecorder = new HashMap() - val Map> fileExtensionToExtensionsSet = new HashMap() - - def Set getRecorders() { - fileExtensionsToRecorder.values.toSet - } - - def boolean hasRecorder(String fileExtension) { - fileExtensionsToRecorder.containsKey(fileExtensionToExtensionsSet.get(fileExtension)) - } - - def ChangeRecorder getRecorder(String fileExtension) { - fileExtensionsToRecorder.get(fileExtensionToExtensionsSet.get(fileExtension)) - } - - def void registerRecorder(Set fileExtensions, ResourceSet recorderResourceSet) { - fileExtensionToExtensionsSet.keySet.forEach [ - checkState(!fileExtensions.contains(it), "there already is a recorder for metamodel %s", it) - ] - val fileExtensionsSet = new HashSet(fileExtensions) - fileExtensions.forEach[fileExtensionToExtensionsSet.put(it, fileExtensionsSet)] - val recorder = new ChangeRecorder(recorderResourceSet) - fileExtensionsToRecorder.put(fileExtensionsSet, recorder) - } - } - - new(VsumFileSystemLayout fileSystemLayout) { - this.fileSystemLayout = fileSystemLayout - this.modelsResourceSet = new ResourceSetImpl().withGlobalFactories() - this.correspondencesResourceSet = new ResourceSetImpl().withGlobalFactories() - this.correspondenceModel = createPersistableCorrespondenceModel(fileSystemLayout.correspondencesURI) - this.modelsResourceSet.eAdapters += new ResourceRegistrationAdapter [ - if(!isLoading) getCreateOrLoadModel(it.URI) - ] - } - - override loadExistingModels() { - isLoading = true - readModelsFile() - correspondenceModel.loadSerializedCorrespondences(modelsResourceSet) - isLoading = false - } - - private def writeModelsFile() { - Files.write(fileSystemLayout.modelsNamesFilesPath, modelsResourceSet.resources.map[URI.toString]) - } - - private def readModelsFile() { - try { - for (modelPath : Files.readAllLines(fileSystemLayout.modelsNamesFilesPath)) { - val uri = URI.createURI(modelPath) - modelsResourceSet.loadOrCreateResource(uri) - createOrLoadModel(uri) - } - } catch (NoSuchFileException e) { - // There are no existing models, so don't do anything - } - } - - override getCorrespondenceModel() { - return CorrespondenceModelViewFactory.createEditableCorrespondenceModelView(correspondenceModel) - } - - override getModel(URI modelURI) { - modelInstances.get(modelURI) - } - - private def getCreateOrLoadModel(URI modelURI) { - getModel(modelURI) ?: createOrLoadModel(modelURI) - } - - def private createOrLoadModel(URI modelURI) { - val resource = if ((modelURI.isFile || modelURI.isPlatform)) { - modelsResourceSet.getOrCreateResource(modelURI) - } else { - modelsResourceSet.loadOrCreateResource(modelURI) - } - val modelInstance = new ModelInstance(resource) - this.modelInstances.put(modelURI, modelInstance) - modelInstance.registerRecorder() - return modelInstance - } - - def private void registerRecorder(ModelInstance modelInstance) { - // Only monitor modifiable models (file / platform URIs, not pathmap URIs) - if (modelInstance.URI.isFile || modelInstance.URI.isPlatform) { - if (!hasRecorder(modelInstance.URI.fileExtension)) { - registerRecorder(#{modelInstance.URI.fileExtension}, modelsResourceSet) - } - val recorder = getRecorder(modelInstance.URI.fileExtension) - recorder.addToRecording(modelInstance.resource) - if (this.isRecording && !recorder.isRecording) { - recorder.beginRecording() - } - } - } - - override void persistAsRoot(EObject rootEObject, URI uri) { - getCreateOrLoadModel(uri).addRoot(rootEObject) - } - - override void saveOrDeleteModels() { - if(logger.isDebugEnabled) logger.debug('''Saving all models of model repository for VSUM «fileSystemLayout»''') - val modelInstancesIterator = modelInstances.entrySet.iterator - while (modelInstancesIterator.hasNext()) { - val modelInstance = modelInstancesIterator.next().value - if (modelInstance.empty) { - modelInstance.delete() - modelInstancesIterator.remove() - } else { - modelInstance.save() - } - } - correspondenceModel.save() - writeModelsFile() - } - - override Iterable recordChanges(Runnable changeApplicator) { - recorders.forEach[beginRecording()] - isRecording = true - logger.debug("Start recording virtual model") - changeApplicator.run() - logger.debug("End recording virtual model") - isRecording = false - recorders.forEach[endRecording()] - return fileExtensionsRecorderMapping.recorders.filter[change.containsConcreteChange].mapFixed[change] - } - - override VitruviusChange applyChange(VitruviusChange change) { - change.resolveAndApply(modelsResourceSet) - } - - override URI getMetadataModelURI(String... metadataKey) { - fileSystemLayout.getConsistencyMetadataModelURI(metadataKey) - } - - override Resource getModelResource(URI uri) { - getCreateOrLoadModel(uri).resource - } - - override close() { - fileExtensionsToRecorder.values.forEach[close()] - modelsResourceSet.resources.forEach[unload] - correspondencesResourceSet.resources.forEach[unload] - modelsResourceSet.resources.clear() - correspondencesResourceSet.resources.clear() - } - - override getModelResources() { - return modelsResourceSet.resources - } - -}