|
|
|
|
|
|
|
Re: Noticing changes on serialized model [message #1808605 is a reply to message #1808568] |
Thu, 27 June 2019 17:00 |
Kai S. Messages: 20 Registered: June 2019 |
Junior Member |
|
|
So I went to the GenPackage and set the "Resource Type" property to "XMI" ("File Extensions" property for me is "designmodel" btw) and regenerated the model code. I then went to the DesignmodelResourceImpl.java and overwrote the useUUIDs function to return true. The plugin.xml of the respective Ecore-project then contained - as you pointed out - this:
<extension point="org.eclipse.emf.ecore.extension_parser">
<!-- @generated designmodel -->
<parser
type="designmodel"
class="mic.model_code_synchronization.designmodel.util.DesignmodelResourceFactoryImpl"/>
</extension>
Where it stops working for me atm I guess is that this Factory does not get used when I create my instances, since when inspecting the XMI it contains nothing like UUIDs and resSet.getID(myDeserializedEObject) returns "null" as well.
Maybe there is a problem with how I create the resource? You wrote this factory would be used automatically when loading resources of the specified file extension (so ".designmodel" in my case), but I never do that. As written earlier I get the underlying Ecore model and its respective factory at runtime via the .ecore-file like this:
ResourceSet rs = new ResourceSetImpl();
rs.getResourceFactoryRegistry().getExtensionToFactoryMap().put("ecore", new XMIResourceFactoryImpl());
Resource res = rs.createResource(URI.createFileURI("C:/.../.../designmodel.ecore"));
res.load(null);
EPackage metapackage = (EPackage) res.getContents().get(0);
EFactory metaFactory = metapackage.getEFactoryInstance();
and then I create the initial XMI resource that gets saved via this "EFactory metaFactory".
So I never load an instance via the specified file extension ".designmodel", which is why I guess the factory that uses UUIDs does not get used and I do not receive the UUIDs in my instances. I only load the .ecore file to receive the ecore-model at runtime, save the initally created instance as an XMI as shown in the next code snippet and reload this XMI later on to notice differences on it:
ResourceSet savingResSet = new ResourceSetImpl();
savingResSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl());
XMIResource savingRes = (XMIResource) savingResSet.createResource(URI.createFileURI(outputPath), null);
savingRes.getDefaultSaveOptions().put(XMIResource.OPTION_KEEP_DEFAULT_CONTENT, Boolean.TRUE);
//adding the created EObjects to this savingRes...
savingRes.save(Collections.EMPTY_MAP);
//this returns null
saving.getID(someEObject_I_created);
So how exactly do I get the factory with the overriden useUUIDs-method to run when I only know at runtime which exact factory this should be? Otherwise I guess I could just rewrite the 2nd line of code above to this
rs.getResourceFactoryRegistry().getExtensionToFactoryMap().put("ecore", new DesignmodelResourceFactoryImpl());
but I do not know this exact factory at design time as already stated. Or is the problem with the way I initially serialize my instance into the XMI (maybe through the casting to XMIResource for the savingRes-variable?)?
I am very sorry for currently not seeing the forest for the trees, but I do not seem to get this to work with my beginner EMF knowledge at the moment.
Thanks a lot for all your time :)
EDIT:
A workaround I thought would be to just call savingRes.setID(myEObject, UUID.randomUUID().toString()); before saving the instance as an XMI. Maybe this will work as well to identify the changed instances, but I would ofc be interested in the "right way" still :)
[Updated on: Thu, 27 June 2019 17:41] Report message to a moderator
|
|
|
|
|
|
|
Re: Noticing changes on serialized model [message #1808862 is a reply to message #1808652] |
Wed, 03 July 2019 12:37 |
Kai S. Messages: 20 Registered: June 2019 |
Junior Member |
|
|
Hey again,
I have a follow-up question:
As stated I want to be able to synchronize the model instance with some internal data it represents. I can now identify changes on the persisted instance (via their UUID) and update the internal data according to user changes at the persisted instance.
I need full CRUD operations though, meaning I want to create, update or delete this internal data according to respective changes to the model instance. Currently I can only update. When trying to synchronize delete or create operations on the model I get errors:
So whenever a user changes the serialized model instance through simple modifications like renaming existing attributes there is no problem. I can then identify the respective old model instance - EObject through the ID and do the transformations on the internal data object according to the user changes to the model instance. But whenever the user deletes a whole element (or adds a new one, its the exact same it seems) in the serialized instance, I always get NULL-returns when trying to retrieve any EObject via their UUID. It seems like something gets broken as soon as the user modifications on the serialized instance go further than simply renaming attributes and it crashes when whole elements get removed or added.
Here the relevant code with some comments:
//method getting called with the user-modified instance
public void updateDataRepresentation(Resource updatedModel) {
updatedModel.getContents().forEach(updatedModelElement -> {
//getting respective model element in existing designmodel (existentDesignmodel is simply a Resource of the previously saved version of the instance)
EObject existentModelElement = existentDesignmodel.getEObject(updatedModel.getID(updatedModelElement));
if(existentModelElement == null) {
//no existend model element there yet, meaning it got added by the user through modifications at the design model
System.out.println(updatedModelElement + " was newly created");
//TODO create data object
return;
}
//MappingEntry is a data object consisting of the EObject and its respective data object it gets mapped to (+ some other stuff)
//see below for these helper-methods
MappingEntry entry = getMappingEntryByModelelement(existentModelElement);
if(entry == null) {
entry = getMappingEntryByID(updatedModel.getID(updatedModelElement));
}
//this is where I change the internal date object according to the (maybe updated) new model element
entry = updateMappingEntry(entry, updatedModelElement);
});
[...] //here I also go through the existentDesignmodel-contents to check if it contains an element that got removed in the updatedModel to then delete it in the respective data objects I maintain
//setting the existent model to the newly updated one after modifying the data objects in my code
this.existentDesignmodel = updatedModel;
}
//helper methods from above
private MappingEntry getMappingEntryByModelelement(EObject modelelement) {
for(MappingEntry e: this.mappings) {
if(e.getDesignmodelElement().equals(modelelement))
return e;
}
return null;
}
private MappingEntry getMappingEntryByID(String id) {
for(MappingEntry e: this.mappings) {
EObject o = e.getDesignmodelElement();
String existentID = this.existentDesignmodel.getID(o);
if(existentID.contentEquals(id))
return e;
}
return null;
}
Now when having added a new element into the model instance or removed a whole element, I always get a NULL-return from from both my helper methods (both trying to find it via the EObject.equals or the UUID). Lets say the serialized instance looks like the following, and then I remove the 2nd State-element with name "Waiting" or add another one etc.:
<designmodel:State xmi:id="cb33c7a3-8769-479e-bbc3-f4b36363b658" name="Ready"/>
<designmodel:State xmi:id="f9ad4afa-1d4f-46cf-982d-d49cdbaf8a90" name="Waiting"/>
I honestly have no idea why they do not work, since they work as intended when just changing attribute-values in the instance (like renaming "Waiting" to "Finished" works totally fine and synchronizes these changes as intended). But as soon as I add or remove whole elements of the serialized instance, they cannot find the existing elements (even for the ones that did not get changed, added, or deleted).
Does anyone have an idea on why that is? Is it maybe something with EMF when adding or removing instance-elements? Or is it something that should work and the error has to lie somewhere else in my own code?
[Updated on: Wed, 03 July 2019 12:40] Report message to a moderator
|
|
|
|
|
Re: Noticing changes on serialized model [message #1809613 is a reply to message #1808943] |
Thu, 18 July 2019 20:31 |
Kai S. Messages: 20 Registered: June 2019 |
Junior Member |
|
|
Hello, it's me again with a related follow-up question in the same program I am still working on :)
In the meanwhile I have added some model-features I want to synchronize with internal data that is modeled as containment-references. I have a class "State" in my ecore-model with a containment-reference "transition" that points to instances of a second type "Transition" with the cardinality 0..* (so implemented as EList<Transition>).
When initially creating the model instance everything works fine and let's say I receive the following serialized version:
<?xml version="1.0" encoding="ASCII"?>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:designmodel="http://www.example.org/designmodel">
<designmodel:State xmi:id="0f359d4a-5154-462c-90aa-e125197cdb6d" name="Ready">
<transition href="#7880aa8f-1e86-42e0-a212-e91326292d31"/>
</designmodel:State>
<designmodel:Transition xmi:id="7880aa8f-1e86-42e0-a212-e91326292d31" name="switchToWaiting"/>
</xmi:XMI>
Deleting or updating the existent Transition-instance works completely fine (via the respective algorithms being called after comparing the 2 lists of xmi:id's as described in the earlier post). But when adding a new Transition-instance to the model I seem to get weird behavior:
Let's say I add another Transition instance, give it some random new xmi:id and reference it in the State-instance, so that it should be contained by it:
<?xml version="1.0" encoding="ASCII"?>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:designmodel="http://www.example.org/designmodel">
<designmodel:State xmi:id="0f359d4a-5154-462c-90aa-e125197cdb6d" name="Ready">
<transition href="#7880aa8f-1e86-42e0-a212-e91326292d31"/>
<transition href="#11111111-2222-3333-4444-ffffffffffff"/>
</designmodel:State>
<designmodel:Transition xmi:id="7880aa8f-1e86-42e0-a212-e91326292d31" name="switchToWaiting"/>
<designmodel:Transition xmi:id="11111111-2222-3333-4444-ffffffffffff" name="userAddedTransition"/>
</xmi:XMI>
Now, when I read in this xmi-version of the instance, iterate over all objects and check for their container via EObject.eContainer(), I receive the containing EObject for all instance-elements but for the ones that the user created. So for the newly added Transition-instance with the attribute name=userAddedTransition the eContainer()-call returns null for some reason. When checking eContents() on the containing State-EObject, I also respectively only get the Transition instances of the transition-reference that were there prior, the user-added one is missing here as well.
It would seem to me that the behavior should be the exact same, since I deserialize all objects from the same xmi and there should be no difference for whether it was there in the first place or inserted by some user action on the model-instance. The added Transition-instance gets recognized correctly and I can also read in their name-attributes etc., but the eContainer for some reason is null and not the containing object I referenced it in. But I cannot think of anything else that would have to be done for it to be recognized correctly as a contained-reference other than adding the <transition href=.../> line referencing the xmi.id of the newly created Transition-instance. Since this is the only thing in the serialized xmi indicating an existing containment-reference from existing objects as well.
Does anyone have an idea on why this could be?
Any help would be greatly appreciated again.
[Updated on: Fri, 19 July 2019 08:31] Report message to a moderator
|
|
|
|
Re: Noticing changes on serialized model [message #1809657 is a reply to message #1809647] |
Fri, 19 July 2019 13:01 |
Kai S. Messages: 20 Registered: June 2019 |
Junior Member |
|
|
How would it look when the State object actually correctly contains Transitions? And how do I achieve that? I currently go about trying to add these like this:
//get target-class of the containment reference
EReference classReference = (EReference) holdingClass.getEStructuralFeature(referenceName);
String targetClassName = classReference.getEReferenceType().getName();
EClass targetClass = (EClass) metapackage.getEClassifier(targetClassName);
//create contained model element object
EFactory metafactory = metapackage.getEFactoryInstance();
EObject targetInstance = metafactory.create(targetClass);
//parentObject in this case would be the State-instance, so I first get the existing contents to then add the new reference to it
List<EObject> refs = new ArrayList<>(parentObject.eContents());
refs.add(targetInstance);
parentObject.eSet(classReference, refs);
But what really confuses me then, is that eContainer() does not return NULL for the referenced objects that got created this way initially, but only for the manually added ones.
The main thing is that I just need to identify from which source object (State objects here) each target object (Transition objects here) get referenced when deserializing the XMI. Even if these are no containments as I have implemented here but only cross references, how would I go about finding the objects that have cross references to them? (Although it seems really weird that eContainer() would return the State-objects for the existing instances when - as you say - this isnt even a containment-reference at all...)
Thanks a lot for your help!
[Updated on: Fri, 19 July 2019 13:16] Report message to a moderator
|
|
|
|
Re: Noticing changes on serialized model [message #1809676 is a reply to message #1809669] |
Fri, 19 July 2019 19:45 |
Kai S. Messages: 20 Registered: June 2019 |
Junior Member |
|
|
classReference.isContainment() is TRUE, this is the reference from State to Transition (so with the name "transition" in the following model). Transition-objects also are only contained in exactly one State-object, so this shouldnt be a problem. The relevant .ecore model looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="designmodel" nsURI="http://www.example.org/designmodel" nsPrefix="designmodel">
<eClassifiers xsi:type="ecore:EClass" name="State">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2003/XMLType#//String"
iD="true"/>
<eStructuralFeatures xsi:type="ecore:EReference" name="transition" upperBound="-1"
eType="#//Transition" containment="true"/>
</eClassifiers>
<eClassifiers xsi:type="ecore:EClass" name="Transition">
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2003/XMLType#//String"
iD="true"/>
</eClassifiers>
</ecore:EPackage>
So this definitely is a containment reference. When parsing in the serialized .xmi-model-instance (after a user has added a new Transition-object and added a containment href just like the existing ones to the State-object), I go about it like this (behaviour in comments):
//loading in the updated model via a helper method as an XMIResource
XMIResource updatedModel = Utility.loadExistingModel(existingModelPath);
//iterating over the objects directly contained in the resource (so all State- and Transition-objects get reached here, since they are root objects themselves)
updatedModel.getContents().forEach(updatedModelElement -> {
System.out.println(updatedModelelement + updatedModelElement.eContainer());
});
I also just realized that I was incorrect formerly, because this eContainer call always returns NULL - also for existing model elements that were not added by the user. I just did not get the error because I used the internal version of the same object if it did not get added by the user (so if updatedModelElement also exists in the prior model version which I still have in memory and compare with to identify newly added elements).
So yeah, you were right, and I guess I never serialize the Transition-objects into the XMI as actual containments. When stepping through the debugger and looking at the XMIResources I can see this too: For the existing one I have in memory, the eContainer's of the Transition-objects are set correctly to the respective State-object, but in the deserialized XMIResource all eContainer-values are NULL.
So my question would then change to: How do I serialize it in a way, that they are actually contained by the State-object i added them to? I posted in my previous post how I go about adding them to the containment-reference via getting the eContents-list, adding the Transition-object to it, and then setting it as the EStructuralFeature "transition" of the State-object. I guess this is where my mistake is, since this does not seem to work as intended.
[Updated on: Sat, 20 July 2019 06:31] Report message to a moderator
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.06864 seconds