Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » Enforce unique IDs (also for deep copies)(how to make sure that no ID(Attribute) is used more than once in a model)
Enforce unique IDs (also for deep copies) [message #1840674] Wed, 21 April 2021 20:45 Go to next message
Thomas Barth is currently offline Thomas BarthFriend
Messages: 22
Registered: March 2021
Junior Member
Hi,

I have a metamodel consisting out of several EMF projects creating a layered overall metamodel.

On the base of the metamodel there is a class which contains an ID attribute.
I have extended the getter of the ID attribute to generate a UUID if the current value of the attribute is null.

I mainly use the generated tree editor. When I copy and paste a object, the ID is also copied, also I want to avoid that the user (or whomever) enters a ID that already is present in the model. Therefore Im currently modifying the setter of the ID attribute.
I first tried to create a static List in the the said base class to contain all IDs and whenever a ID is set it will be checked against this list but because its static and not coupled to the lifetime of the model it changes all IDs if I reload the model. Also I feel like this is messy because I have the IDs twice, once in the model, once in that list.

I then tried to get all objects/IDs in the model and check each new ID against that. ( I hope this will not cost too much performance at startup)
Im a bit lost how to get the ID attribute here (+ not all objects are based on the class with the ID attribute) because I seem to iterate trough references etc but not the objects.
		EStructuralFeature idFeature = this.eClass().getEStructuralFeature("id");

		for (TreeIterator<EObject> i = EcoreUtil.getRootContainer(this).eClass().eAllContents(); i.hasNext(); )
		{
			EObject object = i.next();
			try {
											
				
			}catch (Exception e) {
				// TODO: handle exception
			}
		}


So I need the right methods to iterate trough all objects in the model
or
A better approach.
+
Another (not so important) question:
I found that if I change an attribute of an object programmatically with the generated setter (which by default contains a notification) the Tree Editor doesn't recognize this change and doesn't offer to safe the model to XML, making it very likely for data to be lost. How can I fix this?

Thanks in advance !

[Updated on: Wed, 21 April 2021 20:55]

Report message to a moderator

Re: Enforce unique IDs (also for deep copies) [message #1840675 is a reply to message #1840674] Wed, 21 April 2021 21:12 Go to previous messageGo to next message
Christian Damus is currently offline Christian DamusFriend
Messages: 1270
Registered: July 2009
Location: Canada
Senior Member

Hi, Thomas,

You may be able to use your static collection of known IDs if your genmodel is configured to generate a custom Resource implementation. In that Resource implementation, then, you can override the detachedHelper(EObject) method to remove an object's ID from the static collection to avoid this problem of changing all the IDs again on load. But instead of customizing the setter, you might then also override the attachedHelper(EObject) method of the resource to set an unique ID when an object is first attached, if and only if it doesn't already have one. Then the only problem remaining is the copy-and-paste issue. That can be addressed in your editor by overriding the EditingDomain's copy command to clear the IDs of the copied objects, so that they will be assigned new IDs when pasted, via the resource's attachedHelper method. I think you would have to create a custom EditingDomain implementation (extending AdapterFactoryEditingDomain, of course) but I haven't much experience in doing that.

The problem of the editor not detecting the dirty condition stems from the dirty state being managed via the CommandStack: the editor updates its dirty state when commands are executed, undone, or re-done, together with a pointer that indicates where in the history commands the last save was performed. As much as possible, it's best to implement side-effects in the editor by customizing/decorating/etc. the commands produced by your model's edit ItemProviderAdapters.

HTH,
Christian
Re: Enforce unique IDs (also for deep copies) [message #1840676 is a reply to message #1840675] Wed, 21 April 2021 21:47 Go to previous messageGo to next message
Christian Damus is currently offline Christian DamusFriend
Messages: 1270
Registered: July 2009
Location: Canada
Senior Member

Hi again,

I think it's important to emphasize that it's probably best if any mechanism that generates unique IDs for an object does not happen for an object that already has an ID. This is especially important in loading a resource, in which whatever ID is in the XML must be what is assigned to the object loaded from it. After all, you should be able to load the same resource multiple times in different ResourceSets, all in memory at the same time. It would not be good to have an in-memory copy of a resource changing IDs to make it now different to other in-memory copies of that same resource.

For weird edge cases you will probably still need a validation rule to check for non-unique IDs and flag them to the user. Who knows but some bad merge in Git or something could result in ID clashes despite your tool's best efforts.

I think that for a guide you probably you can look at how the XMLResourceImpl handles generated xmi:ids, which AFAICS behave pretty much exactly how you want your ID attribute to work. See, for example, what the resource does with its eObjectToIDMap and idToEObjectMap.

Christian
Re: Enforce unique IDs (also for deep copies) [message #1840684 is a reply to message #1840676] Thu, 22 April 2021 06:10 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33141
Registered: July 2009
Senior Member
What you're all describing doesn't sound ideal at all. Firstly, generating an ID as a side effect of calling the getter makes me wonder exactly what eIsSet will return before the ID has been generated? That will affect whether the generated ID is even serialized or copied, i.e., if eIsSet is false, the ID won't be serialized. And by default eIsSet will only look at the variable, not call the getter, so it's likely to be wrong.

Modifying the setter sounds even worse. You'd better off modifying the command that create the new objects and initialize them, not mess with the model implementation. A static list of all IDs is definitely not workable because the same model should be usable in multiple independent resource sets. The tree walker will of course only find objects in the tree, but if you're copying an object that is likely added to a tree later and only later will you know what other IDs are in that tree.

Look at org.eclipse.emf.ecore.util.EcoreUtil.getID(EObject) to see how to get the ID of any object.

I think you'd be much better off if you dropped the idea of a modeled ID attribute (intrinsic ID), instead using a resource implementation that overrides org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.useUUIDs() to true which will do everything you're currently trying to manage manually in the model itself (extrinsic ID).

Is there any reason the user ever needs to see the ID in the editor? You can use org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.getID(EObject) to determine the ID the containing resource (if there is one) has assigned to the object, i.e., you could have an unchangeable, volatile, derived attribute that returns the ID (being careful that eResource() might be null and might not be an XMLResource in general).


Ed Merks
Professional Support: https://www.macromodeling.com/
Re: Enforce unique IDs (also for deep copies) [message #1840689 is a reply to message #1840684] Thu, 22 April 2021 07:08 Go to previous messageGo to next message
Ed Willink is currently offline Ed WillinkFriend
Messages: 7655
Registered: July 2009
Senior Member
Hi

Before you can implement an ID you need to have a very very clear idea of what its purpose is and what its properties are. Otherwise you will keep changing the implementation to suit today's flavour of usage.

Who / what are the consumers? a human editor user, a search tool, a persistence engine, a serialization tool, a model idref field?

What is the scope of ID uniqueness? Namespace/Resource/ResourceSet/Thread/Process/Computer/Network/Universe?

Is an ID assigned on creation or lazy first use?

Is an ID assigned by the user / tooling / both?

Once an object has an ID, how much can the object be changed before its ID must be changed?

If an ID is changed, what is the impact on pre-existing references in memory / elsewhere?

If an object evolves to be identical to a pre-existing object should it acquire the same ID as the pre-existing object?

If two users go through the same editing steps in independent editors do their objects have the same IDs?

How does copy and paste work wrt IDs, new or blank?

How does cut and paste work wrt IDs, new, old or blank?

How does backup and restore work wrt IDs, new, old or blank?

In practice I see four main use cases.

1: Casual interactive, so that it is more convenient to search for/debug object "555" or "Operation7" rather than "org.eclipse.project.subsystem.MyClass.myFunction(int)". This is well served by a lazily created transient ID. Possibly just formatting System.identifierHashCode() or a ResourceSet adapter counter.

2. Universally unique such as a GIT commit id. UUID is plausible. But a rewording to correct a commit comment typo is a new commit-id.

3. Resource scoped to make xmi:id more readable/compact. EMF already has powerful Resource options wrt id2eObject maps so exploit them rather than re-implement them. Since xmi:id is significant from save to load, a simple approach is to have a custom MyResourceSaveImpl that assigns aesthetically pleasing xmi:ids immediately prior to save. You can configure XML entities to compress the URI and custom coding to compress the fragment. You can even make the fragment part structurally deterministic.

4. Idiot specification. You were told to do it so you have to but it's gratuitous. Provide a derived property returning EcoreUtil.getURI().

Regards

Ed Willink
Re: Enforce unique IDs (also for deep copies) [message #1840695 is a reply to message #1840689] Thu, 22 April 2021 07:45 Go to previous messageGo to next message
Thomas Barth is currently offline Thomas BarthFriend
Messages: 22
Registered: March 2021
Junior Member
Ed Willink wrote on Thu, 22 April 2021 07:08
Hi

Before you can implement an ID you need to have a very very clear idea of what its purpose is and what its properties are. Otherwise you will keep changing the implementation to suit today's flavour of usage.


You are right, sorry for not pointing that out. The ID only serves internal mechanisms for referencing/primary key in the model and should not be visible to the user at all.

A little bit of background: I received the starting point of the metamodel from an external partner. Im actually an embedded devloper with almost no Java experience, trying to reverse engineer top down a bit whats going on to adjust the metamodel to my needs (generating embedded code with Acceleo). Certainly not the best approach on how to get started with EMF.

Thanks already for the input, Ill try to fight my way trough it.

[Updated on: Thu, 22 April 2021 07:49]

Report message to a moderator

Re: Enforce unique IDs (also for deep copies) [message #1840715 is a reply to message #1840684] Thu, 22 April 2021 11:21 Go to previous messageGo to next message
Thomas Barth is currently offline Thomas BarthFriend
Messages: 22
Registered: March 2021
Junior Member
Ed Merks wrote on Thu, 22 April 2021 06:10
I think you'd be much better off if you dropped the idea of a modeled ID attribute (intrinsic ID), instead using a resource implementation that overrides org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.useUUIDs() to true which will do everything you're currently trying to manage manually in the model itself (extrinsic ID).

Im with you, that looks like the best way to go.
I implemented https://eioki.eu/2007/03/05/how-to-enable-uuid-in-emf-generated-model-to-get-copypaste-working which seems to work. To make sure this thread is complete for future searches (and to get feedgback if its bad style) I attach the code (basically the same as on the page) below.

Ed Merks wrote on Thu, 22 April 2021 06:10
Is there any reason the user ever needs to see the ID in the editor? You can use org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.getID(EObject) to determine the ID the containing resource (if there is one) has assigned to the object, i.e., you could have an unchangeable, volatile, derived attribute that returns the ID (being careful that eResource() might be null and might not be an XMLResource in general).

No its even better if the User doesn't see the the ID.
Just for me (as I try to learn as much as possible). If I generate the attribute with the said settings, the containing object will not contain storage for the said attribute but only a getter in which I would call org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.getID(this)?


multicoreRootLayer is my root project

package multicoreRootLayer.util;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;

public class MulticoreRootLayerResource extends XMIResourceImpl {

	public MulticoreRootLayerResource() {
		super();
	}

	public MulticoreRootLayerResource(URI uri) {
		super(uri);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl#useUUIDs()
	 */
	protected boolean useUUIDs() {
		return true;
	}

}


package multicoreRootLayer.util;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;

public class MulticoreRootLayerResourceFactory extends XMIResourceFactoryImpl{
    public MulticoreRootLayerResourceFactory() {
        super();
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl#createResource(org.eclipse.emf.common.util.URI)
	 */
	public Resource createResource(URI uri) {
	        return new MulticoreRootLayerResource(uri);
	}
}


plugin.xml
 <extension point="org.eclipse.emf.ecore.extension_parser">
      <parser
         type="multicorerootlayer"
         class="multicoreRootLayer.util.MulticoreRootLayerResourceFactory">
      </parser>
   </extension>

[Updated on: Thu, 22 April 2021 11:48]

Report message to a moderator

Re: Enforce unique IDs (also for deep copies) [message #1840722 is a reply to message #1840715] Thu, 22 April 2021 12:05 Go to previous message
Ed Merks is currently offline Ed MerksFriend
Messages: 33141
Registered: July 2009
Senior Member
Yes, your observations about the feature having only a getter is correct; I forgot to mention it should be transient as well, so it doesn't get serialized! So not changeable means no setter, volatile means, no generated field for storage, derived means no-copying, and transient means no serializing.

And yes, your ResourceImpl looks good.


Ed Merks
Professional Support: https://www.macromodeling.com/
Previous Topic:Code Generation
Next Topic:[CDO] CDO client app - can I specify where to get the .options file?
Goto Forum:
  


Current Time: Thu Apr 25 23:41:21 GMT 2024

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

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

Back to the top