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