Skip to content
10 changes: 9 additions & 1 deletion bundles/tools.vitruv.change.atomic/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<EObject, EObject> 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<EObject> resolveAndApplyForward(EChange<HierarchicalId> unresolvedEChange) {
EChange<EObject> resolvedViewResourceChange = resolve(unresolvedEChange, idInUnfilteredModelResolver);
EChange<EObject> 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<EObject> 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<EObject> resolve(EChange<HierarchicalId> unresolvedChange, HierarchicalIdResolver idResolver) {
return AtomicEChangeResolverHelper.resolveChange(unresolvedChange, id -> {
if (unresolvedChange instanceof CreateEObject<HierarchicalId> 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<EObject> eChange) {
if (eChange instanceof FeatureEChange<EObject, ?> featureChange) {
return featureChange.getAffectedElement();
} else if (eChange instanceof EObjectExistenceEChange<EObject> existenceChange) {
return existenceChange.getAffectedElement();
} else if (eChange instanceof InsertRootEObject<EObject> insertChange) {
return insertChange.getNewValue();
} else if (eChange instanceof RemoveRootEObject<EObject> removeChange) {
return removeChange.getOldValue();
}
throw new IllegalArgumentException("cannot identify affected EObject of change %s".formatted(eChange));
}

private static EObject getOldContainedEObject(EChange<EObject> eChange) {
if (eChange instanceof SubtractiveReferenceEChange<EObject> subtractiveChange) {
if (subtractiveChange.isContainment()) {
return subtractiveChange.getOldValue();
}
}
return null;
}

protected static boolean isContainmentChange(EChange<EObject> eChange) {
if (eChange instanceof UpdateReferenceEChange<EObject> referenceChange) {
return referenceChange.isContainment();
}
return false;
}

protected void refreshIds(EObject eObject, HierarchicalIdResolver idResolver) {
idResolver.getAndUpdateId(eObject);
eObject.eContents().forEach(it -> refreshIds(it, idResolver));
}
}
Original file line number Diff line number Diff line change
@@ -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<EObject, EObject> mapCopy2OriginalObject;

public FilterModelResolverImpl(ResourceSet filterResourceSet, ResourceSet preFilterResourceSet, Map<EObject, EObject> 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;
}

}
Original file line number Diff line number Diff line change
@@ -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<EObject> change) {
AtomicEChangeHierarchicalIdResolver atomicChangeResolver = new AtomicEChangeHierarchicalIdResolver(resourceSet);
if (change instanceof CompositeContainerChangeImpl<EObject> compositeChange) {
List<VitruviusChange<EObject>> changes = new LinkedList<>(compositeChange.getChanges());
Collections.reverse(changes);
changes.forEach(it -> applyBackward(resourceSet, it));
} else if (change instanceof TransactionalChangeImpl<EObject> transactionalChange) {
List<EChange<EObject>> 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());
}
}
}
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -48,4 +52,13 @@ public static VitruviusChangeResolver<HierarchicalId> forHierarchicalIds(Resourc
AtomicEChangeHierarchicalIdResolver resolver = new AtomicEChangeHierarchicalIdResolver(resourceSet);
return new VitruviusChangeHierarchicalIdResolver(resolver);
}


public static VitruviusChangeResolver<HierarchicalId> forHierarchicalIdsAndFilteredModel(ResourceSet sourceResourceSet,
ResourceSet filterResourceSet, Map<EObject, EObject> mapCopy2OriginalObject) {
AtomicEChangeHierarchicalIdFilterResolver resolver = new AtomicEChangeHierarchicalIdFilterResolver(filterResourceSet, sourceResourceSet, mapCopy2OriginalObject);
return new VitruviusChangeFilterResolver(resolver);
}


}
Original file line number Diff line number Diff line change
@@ -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<HierarchicalId> {

private AtomicEChangeHierarchicalIdFilterResolver atomicChangeResolver;

public VitruviusChangeFilterResolver(AtomicEChangeHierarchicalIdFilterResolver atomicChangeResolver) {
this.atomicChangeResolver = atomicChangeResolver;
}


@Override
public VitruviusChange<EObject> resolveAndApply(VitruviusChange<HierarchicalId> change) {
return transformVitruviusChange(change, atomicChangeResolver::resolveAndApplyForward,
transactionalChange -> atomicChangeResolver.endTransaction());
}

@Override
public VitruviusChange<HierarchicalId> assignIds(VitruviusChange<EObject> change) {
throw new Error("assigning ids is not supported");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down