Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » Noticing changes on serialized model(Synchronizing internal program state with respective model elements)
Noticing changes on serialized model [message #1808431] Mon, 24 June 2019 13:14 Go to next message
Kai S. is currently offline Kai S.Friend
Messages: 20
Registered: June 2019
Junior Member
Hello everyone,

I have an application build a dynamic model at runtime based on a user-specified meta model and then persist this concrete model in an XMI-format.
The user then may modify this created model like reassigning certain values to existing attributes of classes in the model on the XMI model file.
The model elements in the Ecore model have counter parts in the application itself which need to get modified accordingly when the user changes something in the XMI model file.

I currently have the contents of the old model (as an XMIResource) as well as the contents of the new model after it got modified (also as an XMIResource). I now need a way to find out what changed from the new version to the old one to modify the programmatic counterparts of the model elements, delete them if the model element got deleted or create new ones if a model element got added (basic CRUD methods).
But I do not seem to find a way to identify corresponding elements after a modification of the element in the XMI. How do I know that an element got modified and I need to modify the existing programmatic counterpart, and it isn't a new one of the same type with different parameters? Let me give an example:

Lets say I have an Ecore meta model "designmodel" consisting of a single class "State" that has one attribute "name" of type String. So at runtime a concrete model instance gets built with the name-attribute being set to "Waiting" and saved into an XMI file like so:
<?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 name="Waiting"/>
</xmi:XMI>


Now the model gets modified through a renaming of the name-attribute to the new value "Ready":
<?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 name="Ready"/>
</xmi:XMI>


I now would want to get the corresponding element in the application that represents this State-object and rename its name-value from "Waiting" to "Ready" as well, without changing anything else in it (so I explicitly cannot just generate a new State-Element from scratch and throw away the old one since there is additional data in it that does not get persisted into the XMI).

Is there a way to get something like a unique identifier for the model element after having persisted it as an XMI? I know the EContentAdapter can act as some kind of listener to model changes, but I guess that is only applicable when having the model loaded in the app while it gets changed. But I dont know how my user would be able to change the model if not by modifying the persisted XMI-file. I would need something like a unique ID for each model element, then compare the values of the model element with the same ID from the previous model to the modified one and then act upon the differences between the 2 versions.

Is there any way this could be done? The key requirement for my application is that a model gets created at runtime and the internal data and the model representation of it shall stay synchronized. Meaning when the internal program data gets changed I can just regenerate the model, but when the model changes I need to propagate these changes back to the internal program data somehow. My only thought to implement this and to give the user a way to modify the created model was to persist it as an XMI and then let the user directly modify the XMI. If it is not possible to solve this problem this way I would be open for other solutions as well.


Thanks a lot!
Re: Noticing changes on serialized model [message #1808438 is a reply to message #1808431] Mon, 24 June 2019 15:36 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30691
Registered: July 2009
Senior Member
You can specialize XMIResourceImpl like this:
      new XMIResourceImpl(resourceURI)
      {
        @Override
        protected boolean useUUIDs()
        {
          return true;
        }
      };
This will automatically assign UUIDs to objects as they added to the resource's contents/children. So you just need your own resource factory that always creates your specialized resource and register it in the plugin.xml. You can specify that such a factory be generated in the GenPackage's properties...
Re: Noticing changes on serialized model [message #1808444 is a reply to message #1808438] Mon, 24 June 2019 17:55 Go to previous messageGo to next message
Ed Willink is currently offline Ed WillinkFriend
Messages: 6538
Registered: July 2009
Senior Member
Hi

If your new Resource was modified, useUUIDs() should work as Ed suggested.

If your new Resource was actually recreated you need to work much harder to reuse the old xmi:ids, possibly by exploiting some semantic information. Google my postings on semantically sensiitive xmi:id generation / LUSSID.

Regards

Ed Willink
Re: Noticing changes on serialized model [message #1808445 is a reply to message #1808438] Mon, 24 June 2019 17:57 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
Messages: 20
Registered: June 2019
Junior Member
Thanks a lot for the help!

Do you maybe have some examples/ressources you can link me to for this? I am quite new to the whole EMF eco system and have never done things that go beyond the basic tutorials. How exactly do I specify that such a factory gets generated, and then how do I use it? I currently get a dynamic factory for initially creating the model that gets then serialized and saved as XMI like this:
ResourceSet rs = new ResourceSetImpl();
rs.getResourceFactoryRegistry().getExtensionToFactoryMap().put("ecore", new XMIResourceFactoryImpl());
Resource res = rs.createResource(URI.createFileURI(PATH_TO_ECORE_FILE));
res.load(null);
EPackage metapackage = (EPackage) res.getContents().get(0);
EFactory metaFactory = metapackage.getEFactoryInstance();

I guess this would then change if I need some other form of factory, correct?

And then even more importantly: How do I access these UUIDs when I - as in my case - load in a model via deserializing an XMI-file? Do they get persisted into the XMI-file as some kind of field of each model element that I can then access via some getter-method from each EObject?

Sorry for the probably stupid questions, but I am still feeling a bit lost at the moment.

Thanks a lot for your time :)
Re: Noticing changes on serialized model [message #1808447 is a reply to message #1808445] Mon, 24 June 2019 18:55 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30691
Registered: July 2009
Senior Member
Your examples all shown the serialization of instance resources. You showed nothing about the underlying Ecore models themselves (nor how they are being modified). So there seems to be nothing to imply that the *.ecore models themselves changed., though I imagined that might be the case. You're just not providing enough specific information to make it clear what exactly you are doing.

To answer your specific question, you can access the ID via org.eclipse.emf.ecore.xmi.XMLResource.getID(EObject). But if somehow the user is modifying an Ecore model and also serializing instances according to that modified Ecore model then your question is really about how to detect changes in the Ecore model, not just changes in the instances.
Re: Noticing changes on serialized model [message #1808555 is a reply to message #1808447] Wed, 26 June 2019 19:08 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
Messages: 20
Registered: June 2019
Junior Member
For now I will be happy with just noticing changes on the concrete model instances that got serialized in the XMI. The underlying .ecore-model will not change for now. So I guess this should be doable then with the UUIDs as you described and the getID(EObject) call to then compare their attribute-instance-values from the prior to the new XMI-version.
The last thing I do not quite understand yet is what you said in your previous post here:
Quote:
So you just need your own resource factory that always creates your specialized resource and register it in the plugin.xml. You can specify that such a factory be generated in the GenPackage's properties

So is it not enough to override the useUUIDs()-method when specifying my XMIResourceImpl?I do not seem to find anything related to possible other factories being created, so what exactly do I need to do herefor? And how do I then receive such a factory to build the initial model instance with it?

Thanks a lot :)
Re: Noticing changes on serialized model [message #1808568 is a reply to message #1808555] Thu, 27 June 2019 02:09 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30691
Registered: July 2009
Senior Member
It is enough to override useUUIDs but of course you need to ensure that your specialized resource is used to load and save instances of your instances. If you open your *.genmodel and select the GenPackage---the GenModel is the root of the editor and the GenPackage is its child---the properties include a Resource Type which you can set to XMI. This will generate in your model's util package an XyzResourceFactoryImpl that creates an XyzResourceImpl (also generated, which extends XMIResourceImpl). These you can then specialize. The plugin.xml will contain a registration like this:
   <extension point="org.eclipse.emf.ecore.extension_parser">
      <!-- @generated library -->
      <parser
            type="library"
            class="org.eclipse.example.library.util.LibraryResourceFactoryImpl"/>
   </extension>
The GenPackage's File Extension property allows you to specify the type attribute's value that's generated in the plugin.xml, i.e., the unique file extension that you use for your instances. When running in an Eclipse application, this registration will be available in your resource set's resource factory registry so if you do resourceSet.createResource(URI.createURI("....../instance.library"), your registered resource factory will automatically be used to create the resource. (There's also support for content-type-based resource factory registration, but likely registration of a unique file extension will suffice).
Re: Noticing changes on serialized model [message #1808605 is a reply to message #1808568] Thu, 27 June 2019 17:00 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
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 #1808610 is a reply to message #1808605] Thu, 27 June 2019 18:38 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30691
Registered: July 2009
Senior Member
Now you are talking again about loading Ecore models but you also said, let me quote you "For now I will be happy with just noticing changes on the concrete model instances that got serialized in the XMI. The underlying .ecore-model will not change for now." So why are you showing me code related to loading Ecore models and why are you registering a factory for dealing with *.ecore extensions?

I've also shown you an example resourceSet.createResource(URI.createURI("....../instance.library") so surely you should be using resourceSet.createResource(URI.createURI("....../instance.designmodel") to load your instances But here, in your examples, I just see "outputPath" so I have no idea what file extension "outputPath" might be using. It should be *.designmodel to use your registered factory.

It's also totally unclear what kind of environment you are running in. You should only need to explicitly register resource factories if you are not running an an Eclipse IDE (OSGi runtime).

In the end, the debugger is your best friend. You can step through the logic of what happens in createResource to see how the URI you pass is processed to look up an associated resource factory. Don't neglect your best friend.

Re: Noticing changes on serialized model [message #1808645 is a reply to message #1808610] Fri, 28 June 2019 07:15 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
Messages: 20
Registered: June 2019
Junior Member
The reason I am loading an ecore model with a factory registered for .ecore-files is to load the ecore model once at runtime. My application (which is running inside eclipse as a plugin) loads an ecore-model at runtime initially, then creates an instance of that based on some user input, serializes it as an XMI and then needs to notice changes made to that XMI by the user.
So "outputPath" is something like "C:/.../.../designmodel.xmi" in my example. The ecore model will not get changed after having it loaded once, which is why I only need to then detect modifications on its instances.

I guess I would just have to save my instances as ".designmodel" files and not as xmi, so that the correct factory gets called at runtime.
Re: Noticing changes on serialized model [message #1808646 is a reply to message #1808645] Fri, 28 June 2019 07:36 Go to previous messageGo to next message
Ed Willink is currently offline Ed WillinkFriend
Messages: 6538
Registered: July 2009
Senior Member
Hi

You may use contentType registrations rather than extension registrations so that when a *.xmi is encountered its internal namespace is consulted to determine the appropriate factory.

The contentType support was added to support multiple versions of UML sharing the *.uml extension. The addition retained compatibility retained and so a matching extension registration occludes a matching content type registration. Therefore any match for the default or xmi extension coming from another application can inhibit content types in your application. This limitation can be worked around by registering local content types in the registry for your ResourceRet so that they are examined before delegation prioritizes global extension registrations over global content type registrations. You can find examples of setting up these registrations in UMLResourcesUtil and the UML plugins.

Regards

Ed Willink
Re: Noticing changes on serialized model [message #1808652 is a reply to message #1808646] Fri, 28 June 2019 08:23 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30691
Registered: July 2009
Senior Member
Yes, I'd mentioned content types already, but that's much more complicated to get working so I would strongly suggest using "designmodel" as the file extension for your instance because extension registration is simpler and it performs better, avoiding the need for content type analysis before loading, which must read the root XML element, and avoiding the need to specify the content type when creating an empty new resource (at which point there is obviously nothing to read yet, so it must be explicitly specified when calling org.eclipse.emf.ecore.resourcel.ResourceSet.createResource(URI, String).
Re: Noticing changes on serialized model [message #1808862 is a reply to message #1808652] Wed, 03 July 2019 12:37 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
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 #1808865 is a reply to message #1808862] Wed, 03 July 2019 13:27 Go to previous messageGo to next message
Ed Willink is currently offline Ed WillinkFriend
Messages: 6538
Registered: July 2009
Senior Member
Hi

Surely it's exactly what you should expect?

If you create a new 'left-hand' element attempting to resolve it on the 'right-hand' side will fail - return NULL.

Conversely if you delete a 'left-hand' element attempting to resolve it from the 'right-hand' side will fail - return NULL.

If simple unique xmi:ids are actually sound for your application you just need to start with a list of old xmi:ids, and create a list of new xmi:ids. You then have one of three algorithms to perform per xmi:id.

old and new => refresh
old and not new => delete
new and not old => create

Simple unique xmi:ids are not sound for your application, because your next observation will be that your user deleted something by mistake in the left-hand-side, then recreated it and it got a new xmi:id trashing the right-hand-side. As I observed before, for more than simple modifications you probably need semantically determined xmi:ids rather than random unique ones.

Regards

Ed Willink
Re: Noticing changes on serialized model [message #1808943 is a reply to message #1808865] Thu, 04 July 2019 17:49 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
Messages: 20
Registered: June 2019
Junior Member
Hi,

thanks for the reply. The tip with the two lists of IDs was a good one. There were still some things that made no sense to me initially (not finding elements after performing the update-function more than once), but I could solve them by simply calling my method that builds the model instance again after having synchronized changes from the model back to my internal data. I guess it was just some mess in my code somewhere else and inconsistent states being kept that made the methods return NULL. But I got it working now!

Thank you very much again, I appreciate your help.

Best wishes!
Re: Noticing changes on serialized model [message #1809613 is a reply to message #1808943] Thu, 18 July 2019 20:31 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
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 #1809647 is a reply to message #1809613] Fri, 19 July 2019 09:48 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30691
Registered: July 2009
Senior Member
I don't see that the State object contains Transitions. It has cross references to Transitions. The Transitions are contained directly by the resource and your resource has multiple root objects; one State and two Transitions. Of course these root EObjects have no eContainer(); they only have an eResource().
Re: Noticing changes on serialized model [message #1809657 is a reply to message #1809647] Fri, 19 July 2019 13:01 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
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 #1809669 is a reply to message #1809657] Fri, 19 July 2019 15:08 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30691
Registered: July 2009
Senior Member
Keep in mind that a contained object be be contained in exactly one place so if States are going to share Transitions, they can't use a containment reference. Is classReference.isContainment() true? What reference is this? You've never shared your model so I cannot guess what types of references you all have in it...

org.eclipse.emf.ecore.util.EcoreUtil.UsageCrossReferencer can be used to find all references to an object. org.eclipse.emf.ecore.util.ECrossReferenceAdapter provides a similar function and maintain this information as the model changes so org.eclipse.emf.ecore.util.ECrossReferenceAdapter.getInverseReferences(EObject) will always give the complete answer. You might also look at org.eclipse.emf.ecore.util.EcoreUtil.delete(EObject, boolean) and at org.eclipse.emf.ecore.util.EcoreUtil.remove(EObject) to see how the resource acts as a container...
Re: Noticing changes on serialized model [message #1809676 is a reply to message #1809669] Fri, 19 July 2019 19:45 Go to previous messageGo to next message
Kai S. is currently offline Kai S.Friend
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

Re: Noticing changes on serialized model [message #1809701 is a reply to message #1809676] Sun, 21 July 2019 03:03 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30691
Registered: July 2009
Senior Member
Your containment reference by default is EReference.resolveProxies true so it looks like you're using cross resource containment for them and have added the Transitions not only to State.transitions but also to Resource.getContents(). How did the Transition instances end up being added as root objects of the resource? The EObject.eContainer will be null if the object is not contained by another object. If EObject.resource is also null, only any deserialized instances then EObject.isProxy must be true, i.e., it must be an unresolved (and unresolveable) proxy.
Re: Noticing changes on serialized model [message #1809705 is a reply to message #1809701] Sun, 21 July 2019 13:03 Go to previous message
Kai S. is currently offline Kai S.Friend
Messages: 20
Registered: June 2019
Junior Member
Hey,
you are right, I mistakenly added the contained EObjects to the Resource on their own as well. So they were then contained twice in the serialized model-instance: once without a container and once within the container.
Simply checking for each created EObject whether it is contained by another object to then not add it again (due to it already being implicitly serialized from within its container object) solved everything.

Thanks a lot for your help, I truely appreciate it!
Previous Topic:"EcoreEditPlugin.INSTANCE" sometimes appears in generated XXXEditPlugin.java
Next Topic:Query an EMF model with QVTo
Goto Forum:
  


Current Time: Thu Dec 12 08:31:27 GMT 2019

Powered by FUDForum. Page generated in 0.02530 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top