diff --git a/.gitignore b/.gitignore index 2dc05a6b..6d3a128d 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ target/ .flattened-pom.xml dependency-reduced-pom.xml +# Idea +.idea/ + # Eclipse META-INF build.properties diff --git a/propagation/src/main/xtend/tools/vitruv/change/propagation/ChangePropagationSpecification.xtend b/propagation/src/main/xtend/tools/vitruv/change/propagation/ChangePropagationSpecification.xtend index ed86703d..f4effed9 100644 --- a/propagation/src/main/xtend/tools/vitruv/change/propagation/ChangePropagationSpecification.xtend +++ b/propagation/src/main/xtend/tools/vitruv/change/propagation/ChangePropagationSpecification.xtend @@ -3,6 +3,7 @@ package tools.vitruv.change.propagation import org.eclipse.emf.ecore.EObject import tools.vitruv.change.atomic.EChange import tools.vitruv.change.composite.MetamodelDescriptor +import tools.vitruv.change.composite.description.VitruviusChange; import tools.vitruv.change.correspondence.Correspondence import tools.vitruv.change.correspondence.view.EditableCorrespondenceModelView import tools.vitruv.change.interaction.UserInteractor @@ -52,7 +53,7 @@ interface ChangePropagationSpecification extends ChangePropagationObservable { * @return true if {@link #propagateChange} will perform modifications in response to the * given change, false otherwise */ - def boolean doesHandleChange(EChange change, EditableCorrespondenceModelView correspondenceModel) + def boolean doesHandleChange(EChange change, EditableCorrespondenceModelView correspondenceModel) { false } /** * Performs modifications in target models identified by accessing the given CorrespondenceModel @@ -68,4 +69,35 @@ interface ChangePropagationSpecification extends ChangePropagationObservable { * Must not be null. */ def void propagateChange(EChange change, EditableCorrespondenceModelView correspondenceModel, ResourceAccess resourceAccess) + + + /** + * Returns whether this ChangePropagationSpecification handles the given change, i.e., + * whether {@link #propagateChange} will perform changes when applied to that change. + * + * @return true if {@link #propagateNonAtomicChange} will perform modifications in response to the + * given change, false otherwise. + */ + def boolean doesHandleNonAtomicChanges() { false } + + /** + * If this ChangePropagationSpecification handles non-atomic changes (change sequences), + * see {@link #doesHandleNonAtomicChanges }, this + * performs modifications in target models identified by accessing the given CorrespondenceModel + * for the elements changed by the given EChange in order to reflect the changes in the + * target model. + * + * Else, this does nothing. + * + * @param change the non-atomic change (i.e. change sequence) which shall be propagated. Should affect only elements in + * an instance of a source metamodel of this specification (see + * {@link #getSourceMetamodelDescriptor}). Must not be null. + * @param correspondenceModel the correspondence model to retrieve information about the target + * model from. Must not be null. + * @param resourceAccess an object for resource access, in particular to create new model files. + * Must not be null. + */ + def void propagateNonAtomicChange(VitruviusChange change, EditableCorrespondenceModelView correspondenceModel, ResourceAccess resourceAccess) { + // noop + } } diff --git a/propagation/src/main/xtend/tools/vitruv/change/propagation/impl/ChangePropagator.xtend b/propagation/src/main/xtend/tools/vitruv/change/propagation/impl/ChangePropagator.xtend index 8eea08e2..bbb89c29 100644 --- a/propagation/src/main/xtend/tools/vitruv/change/propagation/impl/ChangePropagator.xtend +++ b/propagation/src/main/xtend/tools/vitruv/change/propagation/impl/ChangePropagator.xtend @@ -87,12 +87,67 @@ class ChangePropagator { val List userInteractions = new ArrayList def private propagateChanges() { - val result = sourceChange.transactionalChangeSequence.flatMapFixed[propagateSingleChange(it)] + /* First, the whole vitruviuschange is propagated to CPSs that can handle it. + * Then, the other CPSs handle the change in their way. + (the non-atomic-enabled CPSs are called, too, but perform noops) + */ + val result = propagateNonAtomicChange() + result += sourceChange.transactionalChangeSequence.flatMapFixed[propagateSingleChange(it)] + handleObjectsWithoutResource() changedResources.forEach[modified = true] return result } + def private List propagateNonAtomicChange() { + val userInteractorChange = installUserInteractorForChange(sourceChange) + changePropagationProvider.forEach[registerObserver(this)] + userInteractor.registerUserInputListener(this) + + val propagationResultChanges = try { + sourceChange.affectedEObjectsMetamodelDescriptors.flatMap [ + // we only want ChangePropSpecs that handle non-atomic changes + changePropagationProvider.getChangePropagationSpecifications(it).filter[it.doesHandleNonAtomicChanges()] => [ + forEach[it.userInteractor = outer.userInteractor] + ] + ].toSet.flatMapFixed [ + propagateNonAtomicChangeForChangePropagationSpecification(sourceChange, it) + ] + } finally { + userInteractor.deregisterUserInputListener(this) + changePropagationProvider.forEach[deregisterObserver(this)] + userInteractorChange.close() + } + + if (logger.isDebugEnabled) { + logger.debug( + '''Propagated «FOR p : propagationPath SEPARATOR ' -> '»«p»«ENDFOR» -> {«FOR changeInPropagation : propagationResultChanges SEPARATOR ", "»« + changeInPropagation.affectedEObjectsMetamodelDescriptors»«ENDFOR»}''' + ) + } + if (logger.isTraceEnabled) { + logger.trace(''' + Result changes: + «FOR result : propagationResultChanges» + «result.affectedEObjectsMetamodelDescriptors»: «result» + «ENDFOR» + ''') + } + + val resultingChanges = new ArrayList() + if (!propagationResultChanges.isNullOrEmpty) { + val propagatedChange = new PropagatedChange(sourceChange, + VitruviusChangeFactory.instance.createCompositeChange(propagationResultChanges)) + resultingChanges += propagatedChange + } + + if (changePropagationMode != ChangePropagationMode.SINGLE_STEP) { + resultingChanges += + propagationResultChanges.filter[it.containsConcreteChange].propagateTransitiveChanges + } + return resultingChanges + } + def private List propagateSingleChange(TransactionalChange change) { checkState(!change.affectedEObjects.isNullOrEmpty, "There are no objects affected by this change:%s%s", System.lineSeparator, change) @@ -163,6 +218,21 @@ class ChangePropagator { return nextPropagations.mapFixed[propagateChanges()].flatten } + def private propagateNonAtomicChangeForChangePropagationSpecification( + VitruviusChange change, + ChangePropagationSpecification propagationSpecification + ) { + val transitiveChanges = modelRepository.recordChanges [ + propagationSpecification.propagateNonAtomicChange(change, modelRepository.correspondenceModel, + modelRepository) + ] + + // Store modification information + changedResources += transitiveChanges.flatMap[it.affectedEObjects].map[eResource].filterNull + + return transitiveChanges + } + def private propagateChangeForChangePropagationSpecification( TransactionalChange change, ChangePropagationSpecification propagationSpecification