Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » Different undo/redo stacks for different elements in the same resource
Different undo/redo stacks for different elements in the same resource [message #1015912] Mon, 04 March 2013 09:13 Go to next message
Cedric Moonen is currently offline Cedric MoonenFriend
Messages: 274
Registered: August 2009
Senior Member
Hello,

I've been using EMF for quite some time now and I often encounter a scenario which is as follow:

I have one model file having some kind of "container" as root element. This container owns a list of elements. Each element is opened in its own editor and need to have its own undo/redo stack. New elements can be opened/added/removed via a kind of explorer view. When the focus is on that view, I also would like to have an undo stack (for the add and remove operations).
When an element is modified in an editor, the changes are saved to the file when the editor is saved. If the editor is closed without saving the changes, all the modifications should be lost.

What I have been doing so far was this:
- each time an element is opened, I copy it and pass the copy to the editor. The editor has its own EditingDomain and thus its own undo/redo stack
- when an editor is saved, I replace the original element in my container with the copy I passed to the editor and save the file. The editor has to make again a copy of the element (otherwise it is now working directly with the model element, and not a copy).

This usually works fine but I feel that this is probably not the best way to do it. Furthermore, there are cases where this can be a problem: if I have adapters registered on an element, they need to point to the new element when the old element is replaced by the new element (when the editor is saved). Additionally, if I have references from elements to another element, those references are broken when the pointed element is replaced by a new one (so, when the file is saved there is a dangling reference exception which is thrown).

Is there a better approach to the problem (I still want to have separate undo/redo stacks for all my editors) ? This seems like a very common issue...

Thank you,
Cédric

[Updated on: Mon, 04 March 2013 09:22]

Report message to a moderator

Re: Editing [message #1015921 is a reply to message #1015912] Mon, 04 March 2013 09:33 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33107
Registered: July 2009
Senior Member
Cedric,

Comments below.

On 04/03/2013 10:13 AM, Cedric Moonen wrote:
> Hello,
>
> I've been using EMF for quite some time now and I often encounter a
> scenario which is as follow:
>
> I have one model file having some kind of "container" as root element.
> This container owns a list of elements. Each element is opened in its
> own editor and need to have its own undo/redo stack.
That sounds tricky...
> New elements can be opened/added/removed via a kind of explorer view.
> When the focus is on that view, I also would like to have an undo
> stack (for the add and remove operations).
> When an element is modified in an editor, the changes are saved to the
> file when the editor is saved. If the editor is closed without saving
> the changes, all the modifications should be lost.
Is everything saved in one resource?
>
> What I have been doing so far was this:
> - each time an element is opened, I copy it and pass the copy to the
> editor. The editor has its own EditingDomain and thus its own
> undo/redo stack
> - when an editor is saved, I replace the original element in my
> container with the copy I passed to the editor and save the file. The
> editor has to make again a copy of the element (otherwise it is now
> working directly with the model element, and not a copy).
How is the container itself saved?
>
> This usually works fine but I feel that this is probably not the best
> way to do it. Furthermore, there are cases where this can be a
> problem: if I have adapters registered on an element, they need to
> point to the new element when the old element is replaced by the new
> element (when the editor is saved).
And you want that to happen only after the editor is saved. Until then
they just see a copy (the original actually) that doesn't change.
> Additionally, if I have references from elements to another element,
> those references are broken when the pointed element is replaced by a
> new one (so, when the file is saved there is a dangling reference
> exception which is thrown).
How many different resources are involved?
>
> Is there a better approach to the problem (I still want to have
> separate undo/redo stacks for all my editors) ? This seems like a very
> common issue...
What you describe makes it sound like they can't be separate. I.e.,
when you delete something, you have to remove all references to it.
>
> Thank you,
> Cédric


Ed Merks
Professional Support: https://www.macromodeling.com/
Re: Editing [message #1015928 is a reply to message #1015921] Mon, 04 March 2013 09:50 Go to previous messageGo to next message
Cedric Moonen is currently offline Cedric MoonenFriend
Messages: 274
Registered: August 2009
Senior Member
Hi Ed,

Thanks for your answer, which is as usual very fast Smile

I will try to clarify a bit. I am here talking about the user perspective (so what I would like to achieve, not taking the technical limitations in account).

There is only one resource (containing only one object being the container) and when I need to save the content of one editor, the element being edited is replacing the old element in the container and then the container is saved in the file (so, when the container is saved, all elements are saved at that time). If there are opened editors with unsaved changes, those changes are not persisted (yet). They will be if the user saves the corresponding editor.

What you describe makes it sound like they can't be separate. I.e., 
when you delete something, you have to remove all references to it.


Indeed, for this part, this is a bit tricky. But here, this could eventually be solved by looking if there are references to the element being deleted and asking the user if he wants to delete it anyway (in which case, the references to the element are also removed). This could work fine, unless there is an editor which is currently opened with unsaved changes and referencing an element being deleted...

How would you solve this problem ? I see that having one general undo/redo stack for everything would solve a lot of technical problems but this is really annoying for the end user (I really don't like the idea that when you undo, you might change something from another editor, or even worse you might change something from an editor which has already been closed)
Re: Editing [message #1015945 is a reply to message #1015928] Mon, 04 March 2013 11:33 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33107
Registered: July 2009
Senior Member
Cedric,

Comments below.

On 04/03/2013 10:50 AM, Cedric Moonen wrote:
> Hi Ed,
>
> Thanks for your answer, which is as usual very fast :)
>
> I will try to clarify a bit. I am here talking about the user
> perspective (so what I would like to achieve, not taking the technical
> limitations in account).
>
> There is only one resource (containing only one object being the
> container) and when I need to save the content of one editor, the
> element being edited is replacing the old element in the container and
> then the container is saved in the file (so, when the container is
> saved, all elements are saved at that time). If there are opened
> editors with unsaved changes, those changes are not persisted (yet).
> They will be if the user saves the corresponding editor.
Those editors could have references to objects that got deleted I suppose...
>
> What you describe makes it sound like they can't be separate. I.e.,
> when you delete something, you have to remove all references to it.
>
> Indeed, for this part, this is a bit tricky. But here, this could
> eventually be solved by looking if there are references to the element
> being deleted and asking the user if he wants to delete it anyway (in
> which case, the references to the element are also removed). This
> could work fine, unless there is an editor which is currently opened
> with unsaved changes and referencing an element being deleted...
One approach you could use is to rely on the fact that EcoreUtil.Copier
keeps a mapping so you could figure out which objects are deleted (have
eResource() null) and you could figure out which original object that
maps to that (you'd have to invert the map). You'd have to remove
references to all those objects. But if there are editors object with
other copies that also refer to those objects, that's still problematic.
>
> How would you solve this problem ? I see that having one general
> undo/redo stack for everything would solve a lot of technical problems
> but this is really annoying for the end user (I really don't like the
> idea that when you undo, you might change something from another
> editor, or even worse you might change something from an editor which
> has already been closed)
It seems there are some trade-offs to be made. The biggest problem
seems to be how to deal with deletion. What I suggested above could
deal with cleaning up the central model, but there can be editors with
copies and I don't know if you want to propagate changes to those
editors. One could certainly imagine doing that, again exploiting the
fact that as you copy, you can maintain a map. But if you propagate
changes to those editors, you'd likely want to flush their command
stacks, even if you do the cleanup with a command, because it doesn't
make sense to undo such a change...


Ed Merks
Professional Support: https://www.macromodeling.com/
Re: Editing [message #1015974 is a reply to message #1015945] Mon, 04 March 2013 13:01 Go to previous messageGo to next message
Cedric Moonen is currently offline Cedric MoonenFriend
Messages: 274
Registered: August 2009
Senior Member
Well, having references from element to other elements is in fact a special case. Usually, I simply have a container with "independent" elements. In that more generic case, which approach would you choose ? Similar as the one I described (making a copy whenever an element is opened and passing the copy to the editor which has its own EditingDomain) ?

For the second problem (having references between elements), I could maybe solve it this way:
- Use a CrossReferencer to find the objects pointing to the element being deleted (the tricky part would be to travel up the object tree and end up to the element container directly under the "container").
- For all those elements, check whether there are editors open. If yes, close those editors (and warn the user about it).
- For all the cross references found, remove them
- Delete the element

In a similar way, when an element is saved (and has references pointing to it):
- Use the CrossReferencer to find the elements pointing to it
- If there are editors open, they will be closed
- Replace all references by the new element
- Save the element.
Re: Editing [message #1015983 is a reply to message #1015974] Mon, 04 March 2013 13:29 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33107
Registered: July 2009
Senior Member
Cedric,

Comments below.

On 04/03/2013 2:01 PM, Cedric Moonen wrote:
> Well, having references from element to other elements is in fact a
> special case. Usually, I simply have a container with "independent"
> elements.
What is the purpose of collecting everything under one container? Why do
you want everything in one resource?
> In that more generic case, which approach would you choose ? Similar
> as the one I described (making a copy whenever an element is opened
> and passing the copy to the editor which has its own EditingDomain) ?
It's the only way to keep changes independent. The Transaction
framework has support for shared editing domains, but I don't know much
about that. It's worth investigating...
>
> For the second problem (having references between elements), I could
> maybe solve it this way:
> - Use a CrossReferencer to find the objects pointing to the element
> being deleted (the tricky part would be to travel up the object tree
> and end up to the element container directly under the "container").
That's just following eContainer links I would think...
> - For all those elements, check whether there are editors open. If
> yes, close those editors (and warn the user about it).
Better might be to flush the stack and apply the changes to that editor
so the user doesn't lose changes...
> - For all the cross references found, remove them
> - Delete the element
>
> In a similar way, when an element is saved (and has references
> pointing to it):
> - Use the CrossReferencer to find the elements pointing to it
> - If there are editors open, they will be closed - Replace all
> references by the new element
> - Save the element.
An ECrossReferenceAdapter might help.


Ed Merks
Professional Support: https://www.macromodeling.com/
Re: Editing [message #1016012 is a reply to message #1015983] Mon, 04 March 2013 15:25 Go to previous messageGo to next message
Cedric Moonen is currently offline Cedric MoonenFriend
Messages: 274
Registered: August 2009
Senior Member
Quote:
What is the purpose of collecting everything under one container? Why do
you want everything in one resource?


It is much more simple for the user: those files are in fact library files that can be created by the user. There should be an easy way to "distribute" those libraries if needed. It is much easier to manage a lot of objects in a library file rather than having a lot of tiny files (something like 500 hundred elements).

Quote:
It's the only way to keep changes independent. The Transaction
framework has support for shared editing domains, but I don't know much
about that. It's worth investigating...


Ok thanks, I'll have a look at it.

Quote:
That's just following eContainer links I would think...


Yes but I need to stop before my top-level container. But yes indeed once I hit the top level container, I return the previous object that was discovered.

Quote:
Better might be to flush the stack and apply the changes to that editor
so the user doesn't lose changes...


Well, I didn't explain it with enough details: the user will be first warned that the editors will be closed and that he will have the opportunity to save his changes or discard his changes (the standard way of closing editors with unsaved changes). He will then have the opportunity to cancel the delete if he doesn't want to save/loose the unsaved changes.

Quote:
An ECrossReferenceAdapter might help.


I'll have a look at it too.

Thanks for all your answers and the time you spend helping people on the forum ! This is really much appreciated Smile
Re: Editing [message #1016108 is a reply to message #1016012] Tue, 05 March 2013 05:49 Go to previous message
Ed Merks is currently offline Ed MerksFriend
Messages: 33107
Registered: July 2009
Senior Member
Cedric,

Comments below.

On 04/03/2013 4:25 PM, Cedric Moonen wrote:
> Quote:
>> What is the purpose of collecting everything under one container? Why
>> do you want everything in one resource?
>
>
> It is much more simple for the user: those files are in fact library
> files that can be created by the user. There should be an easy way to
> "distribute" those libraries if needed. It is much easier to manage a
> lot of objects in a library file rather than having a lot of tiny
> files (something like 500 hundred elements).
How large does such a file become? Saving all 500 elements when only
one tree has changed might be a concern too. Another possible approach
would be to store them as separate files in a single archive. EMF
directly supports access to resources in an archive using archive: or
jar: URIs. There's also support for cross resource containment (you
must enabled it in the GenModel if you're using generated models) so the
root container could be in one resource and each other element in a
separate resource (perhaps all in one archive).
>
> Quote:
>> It's the only way to keep changes independent. The Transaction
>> framework has support for shared editing domains, but I don't know
>> much about that. It's worth investigating...
>
>
> Ok thanks, I'll have a look at it.
>
> Quote:
>> That's just following eContainer links I would think...
>
>
> Yes but I need to stop before my top-level container. But yes indeed
> once I hit the top level container, I return the previous object that
> was discovered.
Exactly.
>
> Quote:
>> Better might be to flush the stack and apply the changes to that
>> editor so the user doesn't lose changes...
>
>
> Well, I didn't explain it with enough details: the user will be first
> warned that the editors will be closed and that he will have the
> opportunity to save his changes or discard his changes (the standard
> way of closing editors with unsaved changes).
The changes might be in conflict. He might have added a reference to an
element that's deleted.
> He will then have the opportunity to cancel the delete if he doesn't
> want to save/loose the unsaved changes.
I imagine it would be nicer for the user if one of the choices was to
merge his changes.
>
> Quote:
>> An ECrossReferenceAdapter might help.
>
>
> I'll have a look at it too.
>
> Thanks for all your answers and the time you spend helping people on
> the forum ! This is really much appreciated :)
You're welcome.


Ed Merks
Professional Support: https://www.macromodeling.com/
Previous Topic:(digraph1, digraph2) import is not available
Next Topic:[Teneo] createRegisterDataStore followed by deRegisterDataStore fails with NPE
Goto Forum:
  


Current Time: Tue Mar 19 02:56:58 GMT 2024

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

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

Back to the top