-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #47 from vitruv-tools/uuid
- Loading branch information
Showing
51 changed files
with
1,785 additions
and
397 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 4 additions & 3 deletions
7
bundles/tools.vitruv.change.atomic/src/tools/vitruv/change/atomic/EChangeIdManager.xtend
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
bundles/tools.vitruv.change.atomic/src/tools/vitruv/change/atomic/EChangeUuidManager.xtend
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package tools.vitruv.change.atomic | ||
|
||
import org.eclipse.emf.ecore.EObject | ||
import tools.vitruv.change.atomic.eobject.CreateEObject | ||
import tools.vitruv.change.atomic.eobject.EObjectAddedEChange | ||
import tools.vitruv.change.atomic.eobject.EObjectExistenceEChange | ||
import tools.vitruv.change.atomic.eobject.EObjectSubtractedEChange | ||
import tools.vitruv.change.atomic.feature.FeatureEChange | ||
import tools.vitruv.change.atomic.uuid.UuidResolver | ||
|
||
import static com.google.common.base.Preconditions.checkArgument | ||
import static com.google.common.base.Preconditions.checkState | ||
|
||
/** | ||
* Provides logic for initializing the UUIDs within changes. | ||
*/ | ||
class EChangeUuidManager { | ||
val UuidResolver uuidResolver | ||
|
||
/** | ||
* Initializes the manager with a {@link UuidResolver}. | ||
* | ||
* @param uuidResolver - | ||
* the {@link UuidResolver} to use for UUID management | ||
*/ | ||
new(UuidResolver uuidResolver) { | ||
checkArgument(uuidResolver !== null, "uuid resolver must not be null") | ||
this.uuidResolver = uuidResolver | ||
} | ||
|
||
def static void setOrGenerateIds(Iterable<EChange> eChanges, UuidResolver uuidResolver) { | ||
setOrGenerateIds(eChanges, uuidResolver, true) | ||
} | ||
|
||
def static void setOrGenerateIds(Iterable<EChange> eChanges, UuidResolver uuidResolver, boolean endTransaction) { | ||
val manager = new EChangeUuidManager(uuidResolver) | ||
eChanges.forEach [ eChange | | ||
manager.setOrGenerateIds(eChange) | ||
] | ||
if (endTransaction) { | ||
uuidResolver.endTransaction | ||
} | ||
} | ||
|
||
def void setOrGenerateIds(EChange eChange) { | ||
switch eChange { | ||
CreateEObject<?>: | ||
setOrGenerateCreatedEObjectUuid(eChange) | ||
EObjectExistenceEChange<?>: | ||
setAffectedEObjectUuid(eChange) | ||
FeatureEChange<?,?>: | ||
setAffectedEObjectUuid(eChange) | ||
} | ||
switch eChange { | ||
EObjectSubtractedEChange<?>: | ||
setOldValueUuid(eChange) | ||
} | ||
switch eChange { | ||
EObjectAddedEChange<?>: | ||
setOrGenerateNewValueUuid(eChange) | ||
} | ||
} | ||
|
||
private def String getUuid(EObject object) { | ||
val id = uuidResolver.getUuid(object) | ||
checkState(id !== null, "uuid must not be null") | ||
return id | ||
} | ||
|
||
private def String getOrGenerateUuid(EObject object) { | ||
if (uuidResolver.hasUuid(object)) { | ||
return getUuid(object) | ||
} | ||
uuidResolver.registerEObject(object) | ||
} | ||
|
||
private def void setOrGenerateNewValueUuid(EObjectAddedEChange<?> addedEChange) { | ||
if (addedEChange.newValue === null) { | ||
return; | ||
} | ||
addedEChange.newValueID = addedEChange.newValue.getOrGenerateUuid | ||
} | ||
|
||
private def void setOldValueUuid(EObjectSubtractedEChange<?> subtractedEChange) { | ||
if (subtractedEChange.oldValue === null) { | ||
return; | ||
} | ||
subtractedEChange.oldValueID = subtractedEChange.oldValue.uuid | ||
} | ||
|
||
private def void setAffectedEObjectUuid(EObjectExistenceEChange<?> existenceChange) { | ||
val affectedEObject = existenceChange.affectedEObject | ||
checkArgument(affectedEObject !== null, "existence change must have an affected EObject: %s", existenceChange) | ||
existenceChange.affectedEObjectID = affectedEObject.uuid | ||
} | ||
|
||
private def void setOrGenerateCreatedEObjectUuid(CreateEObject<?> existenceChange) { | ||
val affectedEObject = existenceChange.affectedEObject | ||
checkArgument(affectedEObject !== null, "existence change must have an affected EObject: %s", existenceChange) | ||
existenceChange.affectedEObjectID = affectedEObject.getOrGenerateUuid | ||
} | ||
|
||
private def void setAffectedEObjectUuid(FeatureEChange<?, ?> featureChange) { | ||
val affectedEObject = featureChange.affectedEObject | ||
checkArgument(affectedEObject !== null, "feature change must have an affected EObject: %s", featureChange) | ||
featureChange.affectedEObjectID = affectedEObject.uuid | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
215 changes: 215 additions & 0 deletions
215
...truv.change.atomic/src/tools/vitruv/change/atomic/resolve/AtomicEChangeUuidResolver.xtend
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
package tools.vitruv.change.atomic.resolve | ||
|
||
import org.eclipse.emf.common.util.URI | ||
import org.eclipse.emf.ecore.EObject | ||
import org.eclipse.emf.ecore.EStructuralFeature | ||
import org.eclipse.emf.ecore.util.EcoreUtil | ||
import tools.vitruv.change.atomic.EChange | ||
import tools.vitruv.change.atomic.eobject.CreateEObject | ||
import tools.vitruv.change.atomic.eobject.DeleteEObject | ||
import tools.vitruv.change.atomic.eobject.EObjectExistenceEChange | ||
import tools.vitruv.change.atomic.feature.FeatureEChange | ||
import tools.vitruv.change.atomic.feature.reference.InsertEReference | ||
import tools.vitruv.change.atomic.feature.reference.RemoveEReference | ||
import tools.vitruv.change.atomic.feature.reference.ReplaceSingleValuedEReference | ||
import tools.vitruv.change.atomic.root.InsertRootEObject | ||
import tools.vitruv.change.atomic.root.RemoveRootEObject | ||
import tools.vitruv.change.atomic.root.RootEChange | ||
import tools.vitruv.change.atomic.uuid.UuidResolver | ||
|
||
import static com.google.common.base.Preconditions.checkArgument | ||
import static com.google.common.base.Preconditions.checkState | ||
|
||
/** | ||
* Static class for resolving EChanges internally. | ||
*/ | ||
package class AtomicEChangeUuidResolver { | ||
val UuidResolver uuidResolver | ||
|
||
new (UuidResolver uuidResolver) { | ||
checkArgument(uuidResolver !== null, "UUID resolver must not be null") | ||
this.uuidResolver = uuidResolver | ||
} | ||
|
||
/** | ||
* Resolves {@link FeatureEChange} attributes {@code affectedEObject} and {@code affectedFeature}. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def private <A extends EObject, F extends EStructuralFeature> void resolveFeatureEChange(FeatureEChange<A, F> change) { | ||
checkArgument(change.affectedEObjectID !== null, "change %s must have an affected EObject ID", change) | ||
checkArgument(change.affectedFeature !== null, "change %s must have an affected feature", change) | ||
if (uuidResolver.hasEObject(change.affectedEObjectID)) { | ||
change.affectedEObject = uuidResolver.getEObject(change.affectedEObjectID) as A | ||
} | ||
change.affectedEObject.checkNotNullAndNotProxy(change, "affected object") | ||
} | ||
|
||
def private EObject resolveObject(String valueId) { | ||
if (valueId === null) { | ||
return null | ||
} | ||
return uuidResolver.getEObject(valueId) | ||
} | ||
|
||
/** | ||
* Resolves {@link EObjectExistenceEChange} attribute {@code affectedEObject}. | ||
* @param change The change which should be resolved. | ||
* @param isNewObject true if the given change creates the object, false if it deletes the object | ||
*/ | ||
def private <A extends EObject> void resolveEObjectExistenceEChange(EObjectExistenceEChange<A> change, boolean isNewObject) { | ||
checkArgument(change.affectedEObjectID !== null, "change %s must have an affected EObject ID", change) | ||
|
||
// Resolve the affected object | ||
if (isNewObject) { | ||
// Check if ID resolver may still contain the removed object | ||
if (uuidResolver.hasEObject(change.affectedEObjectID)) { | ||
val stillExistingObject = uuidResolver.getEObject(change.affectedEObjectID) as A | ||
change.affectedEObject = stillExistingObject | ||
change.affectedEObject.checkNotNullAndNotProxy(change, "affected object") | ||
} else { | ||
// Create new one | ||
val newObject = EcoreUtil.create(change.affectedEObjectType) as A | ||
change.affectedEObject = newObject | ||
uuidResolver.registerEObject(change.affectedEObjectID, newObject) | ||
} | ||
} else { | ||
// Object still exists | ||
change.affectedEObject = uuidResolver.getEObject(change.affectedEObjectID) as A | ||
change.affectedEObject.checkNotNullAndNotProxy(change, "affected object") | ||
} | ||
|
||
if (change.idAttributeValue !== null) { | ||
EcoreUtil.setID(change.affectedEObject, change.idAttributeValue) | ||
} | ||
} | ||
|
||
/** | ||
* Resolves {@link RootEChange} attribute {@code resource}. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def private void resolveRootEChange(RootEChange change) { | ||
// Get resource where the root object will be inserted / removed. | ||
change.resource = uuidResolver.getResource(URI.createURI(change.uri)) | ||
} | ||
|
||
/** | ||
* Resolves the value of an {@link RootEChange}. | ||
* @param change The change whose value shall be resolved. | ||
* @param value The value that should be used if no value can be resolved for an id linked in the given change | ||
* @param isInserted {@code true} if the concrete value is already inserted into the resource. | ||
* Depends on the kind of the change and the model state. | ||
* @returns The resolved value. | ||
*/ | ||
def private <T extends EObject> resolveRootValue(RootEChange change, T value, boolean isInserted) { | ||
// Resolve the root object | ||
val result = if (change instanceof InsertRootEObject<?>) { | ||
uuidResolver.getEObject(change.newValueID) | ||
} else if (change instanceof RemoveRootEObject<?>) { | ||
uuidResolver.getEObject(change.oldValueID) | ||
} else { | ||
value | ||
} | ||
|
||
if (isInserted) { | ||
checkState(0 <= change.index && change.index < change.resource.contents.size && | ||
change.resource.contents.get(change.index) === result, | ||
"invalid index in change %s for resolved object %s", change, result) | ||
} | ||
return result | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link EChange}. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(EChange change) { | ||
// If an EChange reaches this point, there is a dispatch method missing for the concrete type. | ||
throw new UnsupportedOperationException("change of type " + change?.eClass + " is not supported") | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link FeatureEChange} EChange. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(FeatureEChange<EObject, EStructuralFeature> change) { | ||
change.resolveFeatureEChange() | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link InsertEReference} EChange. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(InsertEReference<EObject, EObject> change) { | ||
change.resolveFeatureEChange() | ||
change.newValue = change.newValueID.resolveObject() | ||
change.newValue.checkNotNullAndNotProxy(change, "new value") | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link RemoveEReference} EChange. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(RemoveEReference<EObject, EObject> change) { | ||
change.resolveFeatureEChange() | ||
change.oldValue = change.oldValueID.resolveObject() | ||
change.oldValue.checkNotNullAndNotProxy(change, "old value") | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link ReplaceSingleValuedEReferenceEReference} EChange. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(ReplaceSingleValuedEReference<EObject, EObject> change) { | ||
change.resolveFeatureEChange() | ||
change.newValue = change.newValueID.resolveObject() | ||
change.oldValue = change.oldValueID.resolveObject() | ||
change.oldValue.checkEitherNullOrNotProxy(change, "old value") | ||
change.newValue.checkEitherNullOrNotProxy(change, "new value") | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link InsertRootEObject} EChange. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(InsertRootEObject<EObject> change) { | ||
change.resolveRootEChange() | ||
change.newValue = change.resolveRootValue(change.newValue, false) | ||
change.newValue.checkNotNullAndNotProxy(change, "new value") | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link RemoveRootEObject} EChange. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(RemoveRootEObject<EObject> change) { | ||
change.resolveRootEChange() | ||
change.oldValue = change.resolveRootValue(change.oldValue, true) | ||
change.oldValue.checkNotNullAndNotProxy(change, "old value") | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link CreateEObject} EChange. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(CreateEObject<EObject> change) { | ||
change.resolveEObjectExistenceEChange(true) | ||
} | ||
|
||
/** | ||
* Dispatch method for resolving the {@link DeleteEObject} EChange. | ||
* @param change The change which should be resolved. | ||
*/ | ||
def package dispatch void resolve(DeleteEObject<EObject> change) { | ||
change.resolveEObjectExistenceEChange(false) | ||
} | ||
|
||
def private static void checkNotNullAndNotProxy(EObject object, EChange change, String nameOfElementInChange) { | ||
checkState(object !== null, "%s of change %s was resolved to null", nameOfElementInChange, change) | ||
checkState(!object.eIsProxy, "%s of change %s was resolved to a proxy", nameOfElementInChange, object) | ||
} | ||
|
||
def private static void checkEitherNullOrNotProxy(EObject object, EChange change, String nameOfElementInChange) { | ||
checkState(object === null || !object.eIsProxy, "%s of change %s was resolved to a proxy", nameOfElementInChange, object) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.