diff --git a/bundles/tools.vitruv.change.atomic/META-INF/MANIFEST.MF b/bundles/tools.vitruv.change.atomic/META-INF/MANIFEST.MF index 1d0111797..073bfeea7 100644 --- a/bundles/tools.vitruv.change.atomic/META-INF/MANIFEST.MF +++ b/bundles/tools.vitruv.change.atomic/META-INF/MANIFEST.MF @@ -7,22 +7,30 @@ Bundle-Version: 3.0.1.qualifier Bundle-ClassPath: . Bundle-Vendor: %providerName Bundle-Localization: plugin -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: J2SE-1.5 Export-Package: tools.vitruv.change.atomic, tools.vitruv.change.atomic.eobject, + tools.vitruv.change.atomic.eobject.impl, tools.vitruv.change.atomic.eobject.util, tools.vitruv.change.atomic.feature, tools.vitruv.change.atomic.feature.attribute, + tools.vitruv.change.atomic.feature.attribute.impl, tools.vitruv.change.atomic.feature.attribute.util, + tools.vitruv.change.atomic.feature.impl, tools.vitruv.change.atomic.feature.list, + tools.vitruv.change.atomic.feature.list.impl, tools.vitruv.change.atomic.feature.list.util, tools.vitruv.change.atomic.feature.reference, + tools.vitruv.change.atomic.feature.reference.impl, tools.vitruv.change.atomic.feature.reference.util, tools.vitruv.change.atomic.feature.single, + tools.vitruv.change.atomic.feature.single.impl, tools.vitruv.change.atomic.feature.single.util, tools.vitruv.change.atomic.feature.util, + tools.vitruv.change.atomic.impl, tools.vitruv.change.atomic.hid, tools.vitruv.change.atomic.root, + tools.vitruv.change.atomic.root.impl, tools.vitruv.change.atomic.root.util, tools.vitruv.change.atomic.util, tools.vitruv.change.atomic.uuid diff --git a/bundles/tools.vitruv.change.atomic/src/tools/vitruv/change/atomic/hid/AtomicEChangeHierarchicalIdFilterResolver.java b/bundles/tools.vitruv.change.atomic/src/tools/vitruv/change/atomic/hid/AtomicEChangeHierarchicalIdFilterResolver.java new file mode 100644 index 000000000..714d1c7f2 --- /dev/null +++ b/bundles/tools.vitruv.change.atomic/src/tools/vitruv/change/atomic/hid/AtomicEChangeHierarchicalIdFilterResolver.java @@ -0,0 +1,140 @@ +package tools.vitruv.change.atomic.hid; + +import static com.google.common.base.Preconditions.checkState; + +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.util.EcoreUtil; + +import tools.vitruv.change.atomic.EChange; +import tools.vitruv.change.atomic.command.internal.ApplyEChangeSwitch; +import tools.vitruv.change.atomic.eobject.CreateEObject; +import tools.vitruv.change.atomic.eobject.EObjectExistenceEChange; +import tools.vitruv.change.atomic.feature.FeatureEChange; +import tools.vitruv.change.atomic.feature.reference.SubtractiveReferenceEChange; +import tools.vitruv.change.atomic.feature.reference.UpdateReferenceEChange; +import tools.vitruv.change.atomic.hid.internal.FilterModelResolverImpl; +import tools.vitruv.change.atomic.hid.internal.HierarchicalIdResolver; +import tools.vitruv.change.atomic.hid.internal.HierarchicalIdResolverImpl; +import tools.vitruv.change.atomic.resolve.AtomicEChangeResolverHelper; +import tools.vitruv.change.atomic.root.InsertRootEObject; +import tools.vitruv.change.atomic.root.RemoveRootEObject; + +/** + * A resolver for resolving a change with {@link HierarchicalId} which has been executed on + * a filtered model in a specific FilterResourceSet and not the unfilteredResourceSet (the resourceSet of the + * unfiltered model). Resolves changes + * by searching objects in the given FilterResourceSet first and then mapping it to the unfilteredResourceSet. + * The filterResourceSet must be in the state before the change has been applied. + * This Resolver must only be used for resolveAndApplyForward, but not for applyForwardAndAssignIds as this might + * produce unexpected behaviour. + */ +public class AtomicEChangeHierarchicalIdFilterResolver { + + /** + * The resolver which is used to resolve HierarchicalIds in the unfiltered ResourceSet. + */ + protected HierarchicalIdResolver idInUnfilteredModelResolver; + + /** + * The resolver which is used to resolve HierarchicalIds + */ + private HierarchicalIdResolver idInFilteredModelResolver; + + + public AtomicEChangeHierarchicalIdFilterResolver(ResourceSet filterResourceSet, ResourceSet unfilteredResourceSet, Map mapCopy2OriginalObject) { + idInUnfilteredModelResolver = new FilterModelResolverImpl(filterResourceSet, unfilteredResourceSet, mapCopy2OriginalObject); + idInFilteredModelResolver = new HierarchicalIdResolverImpl(filterResourceSet); + } + + + /** + * Resolves the given change using its {@link HierarchicalIdResolver} and + * applies it forward. The associated resource set must be in the state before + * the change has been applied. + * + * @param unresolvedEChange the change to resolve and apply. + * @return Returns the resolved change. + */ + public EChange resolveAndApplyForward(EChange unresolvedEChange) { + EChange resolvedViewResourceChange = resolve(unresolvedEChange, idInUnfilteredModelResolver); + EChange resolvedFilterResourceChange = resolve(unresolvedEChange, idInFilteredModelResolver); + applyForward(resolvedViewResourceChange, idInUnfilteredModelResolver); + applyForward(resolvedFilterResourceChange, idInFilteredModelResolver); + return resolvedViewResourceChange; + } + + /** + * Ends a transactions such that all {@link EObject}s not being contained in a + * resource, which is contained in a resource set, are removed from the + * hierarchical ID mapping. + */ + public void endTransaction() { + idInUnfilteredModelResolver.endTransaction(); + idInFilteredModelResolver.endTransaction(); + } + + + private void applyForward(EChange resolvedChange, HierarchicalIdResolver idResolver) { + EObject affectedEObject = getAffectedEObject(resolvedChange); + HierarchicalId affectedId = idResolver.getAndUpdateId(affectedEObject); + EObject oldObject = getOldContainedEObject(resolvedChange); + ApplyEChangeSwitch.applyEChange(resolvedChange, true); + if (isContainmentChange(resolvedChange) || affectedId != idResolver.getAndUpdateId(affectedEObject)) { + refreshIds(affectedEObject, idResolver); + } + if (oldObject != null) { + refreshIds(oldObject, idResolver); + } + } + + private EChange resolve(EChange unresolvedChange, HierarchicalIdResolver idResolver) { + return AtomicEChangeResolverHelper.resolveChange(unresolvedChange, id -> { + if (unresolvedChange instanceof CreateEObject createChange) { + EObject createdElement = EcoreUtil.create(createChange.getAffectedEObjectType()); + HierarchicalId createdId = idResolver.getAndUpdateId(createdElement); + checkState(createdId.equals(id), + "generated ID %s does not match the original ID %s on element creation", createdId, id); + return createdElement; + } else { + return idResolver.getEObject(id); + } + }, resource -> idResolver.getResource(resource.getURI())); + } + + private static EObject getAffectedEObject(EChange eChange) { + if (eChange instanceof FeatureEChange featureChange) { + return featureChange.getAffectedElement(); + } else if (eChange instanceof EObjectExistenceEChange existenceChange) { + return existenceChange.getAffectedElement(); + } else if (eChange instanceof InsertRootEObject insertChange) { + return insertChange.getNewValue(); + } else if (eChange instanceof RemoveRootEObject removeChange) { + return removeChange.getOldValue(); + } + throw new IllegalArgumentException("cannot identify affected EObject of change %s".formatted(eChange)); + } + + private static EObject getOldContainedEObject(EChange eChange) { + if (eChange instanceof SubtractiveReferenceEChange subtractiveChange) { + if (subtractiveChange.isContainment()) { + return subtractiveChange.getOldValue(); + } + } + return null; + } + + protected static boolean isContainmentChange(EChange eChange) { + if (eChange instanceof UpdateReferenceEChange referenceChange) { + return referenceChange.isContainment(); + } + return false; + } + + protected void refreshIds(EObject eObject, HierarchicalIdResolver idResolver) { + idResolver.getAndUpdateId(eObject); + eObject.eContents().forEach(it -> refreshIds(it, idResolver)); + } +} diff --git a/bundles/tools.vitruv.change.atomic/src/tools/vitruv/change/atomic/hid/internal/FilterModelResolverImpl.java b/bundles/tools.vitruv.change.atomic/src/tools/vitruv/change/atomic/hid/internal/FilterModelResolverImpl.java new file mode 100644 index 000000000..19907e20a --- /dev/null +++ b/bundles/tools.vitruv.change.atomic/src/tools/vitruv/change/atomic/hid/internal/FilterModelResolverImpl.java @@ -0,0 +1,45 @@ +package tools.vitruv.change.atomic.hid.internal; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.ResourceSet; + +import tools.vitruv.change.atomic.hid.HierarchicalId; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +import java.util.Map; + +/** + * This is a special {@link HierarchicalIdResolver} which can be used to resolve {@link HierarchicalId}s on Filtered-Models. + * The Resolver therefore needs the filtered {@link ResourceSet} as well as the unfiltered {@link ResourceSet}. The Resolver first tries to + * resolve a given {@link HierarchicalId} in the filtered {@link ResourceSet}. If the {@link HierarchicalId} can be resolved in the + * filtered {@link ResourceSet}, the Resolver uses the mapping-function, which has been provided during construction + * {@link mapCopy2OriginalObject}, to find the corresponding {@link EObject} in the unfiltered {@link ResourceSet}. + */ +public class FilterModelResolverImpl extends HierarchicalIdResolverImpl { + + private Map mapCopy2OriginalObject; + + public FilterModelResolverImpl(ResourceSet filterResourceSet, ResourceSet preFilterResourceSet, Map mapCopy2OriginalObject) { + super(filterResourceSet); + this.mapCopy2OriginalObject = mapCopy2OriginalObject; + checkArgument(filterResourceSet != null, "Resource set may not be null"); + } + + + @Override + public EObject getEObject(HierarchicalId id) { + EObject objectInFilterSet = super.getEObject(id); + EObject eObject = getEObjectInUnfilteredSet(objectInFilterSet); + checkState(eObject != null, "no EObject could be found for ID: %s", id); + return eObject; + } + + + private EObject getEObjectInUnfilteredSet(EObject objectInFilteredSet) { + EObject eObjectInViewResourceSet = mapCopy2OriginalObject.get(objectInFilteredSet); + return eObjectInViewResourceSet; + } + +} diff --git a/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/VitruviusChangeBackwardsExecutionHelper.java b/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/VitruviusChangeBackwardsExecutionHelper.java new file mode 100644 index 000000000..3bb020173 --- /dev/null +++ b/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/VitruviusChangeBackwardsExecutionHelper.java @@ -0,0 +1,45 @@ +package tools.vitruv.change.composite.description; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.ResourceSet; + +import tools.vitruv.change.atomic.EChange; +import tools.vitruv.change.atomic.hid.AtomicEChangeHierarchicalIdResolver; +import tools.vitruv.change.composite.description.impl.CompositeContainerChangeImpl; +import tools.vitruv.change.composite.description.impl.TransactionalChangeImpl; + +/** + * Utility class to perform a {@link VitruvChange} backwards on a given {@link ResourceSet}. + */ +public class VitruviusChangeBackwardsExecutionHelper { + + /** + * Applies the given {@link VitruvChange} backwards on the given {@link ResourceSet} The given + * {@link VitruvChange} must + * only reference {@link EObject}s in the given {@link ResourceSet}. After the execution, the + * given {@link ResourceSet} will be in the state before the execution of the change. The + * given {@link ResourceSet} must be in the state after the execution of the given {@link VitruvChange}. + * + * @param resourceSet The resourceSet on which the change is supposed to be executed backwards + * @param change The change which is supposed to be executed backwards + */ + public static void applyBackward(ResourceSet resourceSet, VitruviusChange change) { + AtomicEChangeHierarchicalIdResolver atomicChangeResolver = new AtomicEChangeHierarchicalIdResolver(resourceSet); + if (change instanceof CompositeContainerChangeImpl compositeChange) { + List> changes = new LinkedList<>(compositeChange.getChanges()); + Collections.reverse(changes); + changes.forEach(it -> applyBackward(resourceSet, it)); + } else if (change instanceof TransactionalChangeImpl transactionalChange) { + List> changes = new LinkedList<>(transactionalChange.getEChanges()); + Collections.reverse(changes); + changes.forEach(atomicChangeResolver::applyBackward); + } else { + throw new IllegalStateException( + "trying to apply unknown change of class " + change.getClass().getSimpleName()); + } + } +} diff --git a/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/VitruviusChangeResolver.java b/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/VitruviusChangeResolver.java index 99b61826e..c3db5244f 100644 --- a/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/VitruviusChangeResolver.java +++ b/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/VitruviusChangeResolver.java @@ -1,13 +1,17 @@ package tools.vitruv.change.composite.description; +import java.util.Map; + import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; +import tools.vitruv.change.atomic.hid.AtomicEChangeHierarchicalIdFilterResolver; import tools.vitruv.change.atomic.hid.AtomicEChangeHierarchicalIdResolver; import tools.vitruv.change.atomic.hid.HierarchicalId; import tools.vitruv.change.atomic.uuid.AtomicEChangeUuidResolver; import tools.vitruv.change.atomic.uuid.Uuid; import tools.vitruv.change.atomic.uuid.UuidResolver; +import tools.vitruv.change.composite.description.impl.VitruviusChangeFilterResolver; import tools.vitruv.change.composite.description.impl.VitruviusChangeHierarchicalIdResolver; import tools.vitruv.change.composite.description.impl.VitruviusChangeUuidResolver; @@ -48,4 +52,13 @@ public static VitruviusChangeResolver forHierarchicalIds(Resourc AtomicEChangeHierarchicalIdResolver resolver = new AtomicEChangeHierarchicalIdResolver(resourceSet); return new VitruviusChangeHierarchicalIdResolver(resolver); } + + + public static VitruviusChangeResolver forHierarchicalIdsAndFilteredModel(ResourceSet sourceResourceSet, + ResourceSet filterResourceSet, Map mapCopy2OriginalObject) { + AtomicEChangeHierarchicalIdFilterResolver resolver = new AtomicEChangeHierarchicalIdFilterResolver(filterResourceSet, sourceResourceSet, mapCopy2OriginalObject); + return new VitruviusChangeFilterResolver(resolver); + } + + } diff --git a/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/impl/VitruviusChangeFilterResolver.java b/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/impl/VitruviusChangeFilterResolver.java new file mode 100644 index 000000000..7c1615b0b --- /dev/null +++ b/bundles/tools.vitruv.change.composite/src/tools/vitruv/change/composite/description/impl/VitruviusChangeFilterResolver.java @@ -0,0 +1,33 @@ +package tools.vitruv.change.composite.description.impl; + +import org.eclipse.emf.ecore.EObject; + +import tools.vitruv.change.atomic.hid.AtomicEChangeHierarchicalIdFilterResolver; +import tools.vitruv.change.atomic.hid.HierarchicalId; +import tools.vitruv.change.composite.description.VitruviusChange; + +/** + * This Resolver can be used to resolve {@link VitruvChange} Objects which have been made on a + * filtered {@link ResourceSet} on the unfiltered {@link ResourceSet}. The resolver uses {@link HierarchicalId}s. + * May not be used to assign Ids but only to resolve Changes which reference {@link HierarchicalId}s. + */ +public class VitruviusChangeFilterResolver extends AbstractVitruviusChangeResolver { + + private AtomicEChangeHierarchicalIdFilterResolver atomicChangeResolver; + + public VitruviusChangeFilterResolver(AtomicEChangeHierarchicalIdFilterResolver atomicChangeResolver) { + this.atomicChangeResolver = atomicChangeResolver; + } + + + @Override + public VitruviusChange resolveAndApply(VitruviusChange change) { + return transformVitruviusChange(change, atomicChangeResolver::resolveAndApplyForward, + transactionalChange -> atomicChangeResolver.endTransaction()); + } + + @Override + public VitruviusChange assignIds(VitruviusChange change) { + throw new Error("assigning ids is not supported"); + } +} diff --git a/bundles/tools.vitruv.change.correspondence/META-INF/MANIFEST.MF b/bundles/tools.vitruv.change.correspondence/META-INF/MANIFEST.MF index 970688abd..5334d8177 100644 --- a/bundles/tools.vitruv.change.correspondence/META-INF/MANIFEST.MF +++ b/bundles/tools.vitruv.change.correspondence/META-INF/MANIFEST.MF @@ -5,7 +5,7 @@ Bundle-SymbolicName: tools.vitruv.change.correspondence;singleton:=true Automatic-Module-Name: tools.vitruv.change.correspondence Bundle-ClassPath: . Bundle-Version: 3.0.1.qualifier -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.apache.log4j, org.eclipse.emf.ecore.change, edu.kit.ipd.sdq.commons.util.emf, diff --git a/bundles/tools.vitruv.change.interaction.model/META-INF/MANIFEST.MF b/bundles/tools.vitruv.change.interaction.model/META-INF/MANIFEST.MF index 0230632b1..58b5209e9 100644 --- a/bundles/tools.vitruv.change.interaction.model/META-INF/MANIFEST.MF +++ b/bundles/tools.vitruv.change.interaction.model/META-INF/MANIFEST.MF @@ -7,7 +7,7 @@ Bundle-Version: 3.0.1.qualifier Bundle-ClassPath: . Bundle-Vendor: %providerName Bundle-Localization: plugin -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: tools.vitruv.change.interaction, tools.vitruv.change.interaction.impl, tools.vitruv.change.interaction.util diff --git a/bundles/tools.vitruv.testutils.metamodels/META-INF/MANIFEST.MF b/bundles/tools.vitruv.testutils.metamodels/META-INF/MANIFEST.MF index 301ad4a50..6fa275b5c 100644 --- a/bundles/tools.vitruv.testutils.metamodels/META-INF/MANIFEST.MF +++ b/bundles/tools.vitruv.testutils.metamodels/META-INF/MANIFEST.MF @@ -5,7 +5,7 @@ Bundle-SymbolicName: tools.vitruv.testutils.metamodels;singleton:=true Automatic-Module-Name: tools.vitruv.testutils.metamodels Bundle-Version: 3.0.1.qualifier Bundle-ClassPath: . -Bundle-RequiredExecutionEnvironment: JavaSE-17 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: allElementTypes, allElementTypes.impl, allElementTypes.util,