Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » Implementing Undo properly with multiple EditingDomain and lots of commands
Implementing Undo properly with multiple EditingDomain and lots of commands [message #501928] Fri, 04 December 2009 13:53 Go to next message
Guillaume Chatelet is currently offline Guillaume ChateletFriend
Messages: 146
Registered: July 2009
Senior Member
Hello Ed,

I tried to implement undo/redo on my model for a few weeks now but I can't seem to find the correct approach. So your insights and advices could be precious to me.
My model is quite unusual so I will explain it first.


1/ The context
==============

Basically, I have a Project class containing a list of Element and an index.

Project
-------
+ elements : Element 0..*
+ index : int
+ properties : MapEntry 0..*

Element
-------
+ properties : MapEntry 0..*

My Elements and my project have a property map to store key/value pairs.

Project's index is referring to which Element is currently being edited within the project.

The user is able to change the Project's index and to perform modifications on the currently selected Element (ie : project.getElements().getAt(index) ).
Once selected, the modifications performed within the element are local to it ( one undo stack per elements ).

The user can then set or remove key/value pairs by the mean of a trackball like device ( = the device events will translate to model events )
Then a few adapters on Elements will react to those changes and update the GUI. So there will be a lot of model modifications and this is intended to be interactive.

Then I'd like to add undo/redo/copy/paste support to this model.

If the device is not activated for say 500ms, then a new undo state is stacked on the Element's command stack.

2/ The tested designs
=====================

-1- First approach listening to changes with a ChangeRecorder

I first tried to adapt my current Element with a ChangeRecorder so every modification will be tracked and recorded. Once reached the 500ms of inactivity, this ChangeRecorder is ended and generates a ChangeDescription. Encapsulating the ChangeDescription in a Command allowed me to implement the undo/redo.
The issue with this design is : undoing generates a new model modification leading to a new ChangeDescription appended to my CommandStack...

-2- Everything with commands

I then tried to perform all the Element modifications through commands. Those commands are gathered through a CompoundCommand ( appendAndExecute ). Once the inactivity timeout detected, the CompoundCommand is executed on the Element's CommandStack.
The issue with this design are :
+ The last command is executed twice : once with CompoundCommand.appendAndExecute(), and once within commandStack.execute(compoundCmd)
+ CompoundCommand can contain 5000 Commands thus creating a lot of unnecessary objects and intermediate states
+ Maybe I can create an OptimizedCompoundCommand that merges similar Commands to keep the CompoundCommand small ?

3/ Another untested design
==========================

-1- Using EMF.Compare

This is my last idea so far but I haven't implemented it yet. I plan to make a copy of the Element before the first modification, then as the timeout occurs I plan to use EMF.Compare to track the differences between the first and last state, storing the differences in a Command to add it to the CommandStack.


4/ The questions
================

Do you think this last design will do ? Do you see any inconvenient with this method ?
How can I provide one CommandStack per Element ? for the moment I'm using an AdapterFactoryEditingDomain but only one CommandStack is associated with it so I'm planning to explicitely add a CommandStack reference to my Element class.
Will it integrates nicely with Copy/Paste commands ?

I'm sorry for this rather long post but I'm running out of ideas right now and I think I need help.

Best regards,
Guillaume
Re: Implementing Undo properly with multiple EditingDomain and lots of commands [message #501935 is a reply to message #501928] Fri, 04 December 2009 14:17 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 30620
Registered: July 2009
Senior Member
Guillaume,

Comments below.


Guillaume CHATELET wrote:
> Hello Ed,
>
> I tried to implement undo/redo on my model for a few weeks now but I
> can't seem to find the correct approach.
The generated editor does this out of the box though...
> So your insights and advices could be precious to me.
> My model is quite unusual so I will explain it first.
>
>
> 1/ The context
> ==============
>
> Basically, I have a Project class containing a list of Element and an
> index.
>
> Project
> -------
> + elements : Element 0..*
> + index : int
> + properties : MapEntry 0..*
>
> Element
> -------
> + properties : MapEntry 0..*
>
> My Elements and my project have a property map to store key/value pairs.
>
> Project's index is referring to which Element is currently being
> edited within the project.
>
> The user is able to change the Project's index and to perform
> modifications on the currently selected Element (ie :
> project.getElements().getAt(index) ).
> Once selected, the modifications performed within the element are
> local to it ( one undo stack per elements ).
>
> The user can then set or remove key/value pairs by the mean of a
> trackball like device ( = the device events will translate to model
> events )
> Then a few adapters on Elements will react to those changes and update
> the GUI. So there will be a lot of model modifications and this is
> intended to be interactive.
>
> Then I'd like to add undo/redo/copy/paste support to this model.
>
> If the device is not activated for say 500ms, then a new undo state is
> stacked on the Element's command stack.
>
> 2/ The tested designs
> =====================
>
> -1- First approach listening to changes with a ChangeRecorder
>
> I first tried to adapt my current Element with a ChangeRecorder so
> every modification will be tracked and recorded. Once reached the
> 500ms of inactivity, this ChangeRecorder is ended and generates a
> ChangeDescription. Encapsulating the ChangeDescription in a Command
> allowed me to implement the undo/redo.
> The issue with this design is : undoing generates a new model
> modification leading to a new ChangeDescription appended to my
> CommandStack...
Are you using a ChangeCommand? While undoing you're clearly need to
stop recording changes...
>
> -2- Everything with commands
>
> I then tried to perform all the Element modifications through
> commands. Those commands are gathered through a CompoundCommand (
> appendAndExecute ). Once the inactivity timeout detected, the
> CompoundCommand is executed on the Element's CommandStack.
But at this point you've already done all the things?
> The issue with this design are :
> + The last command is executed twice : once with
> CompoundCommand.appendAndExecute(), and once within
> commandStack.execute(compoundCmd)
Yes, I imagine that's the case.
> + CompoundCommand can contain 5000 Commands thus creating a lot of
> unnecessary objects and intermediate states
Is this an actual performance issue you need to address?
> + Maybe I can create an OptimizedCompoundCommand that merges similar
> Commands to keep the CompoundCommand small ?
>
> 3/ Another untested design
> ==========================
>
> -1- Using EMF.Compare
>
> This is my last idea so far but I haven't implemented it yet. I plan
> to make a copy of the Element before the first modification, then as
> the timeout occurs I plan to use EMF.Compare to track the differences
> between the first and last state, storing the differences in a Command
> to add it to the CommandStack.
I can't imagine that working better than a change recorder.
>
>
> 4/ The questions
> ================
>
> Do you think this last design will do ? Do you see any inconvenient
> with this method ?
It sounds not so good.
> How can I provide one CommandStack per Element ? for the moment I'm
> using an AdapterFactoryEditingDomain but only one CommandStack is
> associated with it so I'm planning to explicitely add a CommandStack
> reference to my Element class.
If you want separate undos, you'd definitely need to keep them separate.
> Will it integrates nicely with Copy/Paste commands ?
It all sounds a little confusing. I'm not sure how, for example, the
user can control what gets undone when they invoke undo.
>
> I'm sorry for this rather long post but I'm running out of ideas right
> now and I think I need help.
I'm really not sure what to suggest given the complex design you've
outlined. Probably looking at ChangeCommand will help, but your
scenario is tricky because you don't do everything while executing the
command; you keep doing things and eventually you want to indicate that
the command is complete and the changes should be rolled up and captured...
>
> Best regards,
> Guillaume
Re: Implementing Undo properly with multiple EditingDomain and lots of commands [message #501984 is a reply to message #501935] Fri, 04 December 2009 16:15 Go to previous messageGo to next message
Guillaume Chatelet is currently offline Guillaume ChateletFriend
Messages: 146
Registered: July 2009
Senior Member
Ed Merks wrote:
>> I tried to implement undo/redo on my model for a few weeks now but I
>> can't seem to find the correct approach.
> The generated editor does this out of the box though...

I should have mentioned I'm not within Eclipse but in a pure Java application using EMF. I have complicated GUI issues I can't integrate easily within Eclipse.

>> 2/ The tested designs
>> =====================
>>
>> -1- First approach listening to changes with a ChangeRecorder
>>
>> I first tried to adapt my current Element with a ChangeRecorder so
>> every modification will be tracked and recorded. Once reached the
>> 500ms of inactivity, this ChangeRecorder is ended and generates a
>> ChangeDescription. Encapsulating the ChangeDescription in a Command
>> allowed me to implement the undo/redo.
>> The issue with this design is : undoing generates a new model
>> modification leading to a new ChangeDescription appended to my
>> CommandStack...
> Are you using a ChangeCommand? While undoing you're clearly need to
> stop recording changes...
I tried the ChangeCommand yes and also a custom command ( as I discovered ChangeCommand lately )
Maybe this is my best option right now.
>> -2- Everything with commands
>>
>> I then tried to perform all the Element modifications through
>> commands. Those commands are gathered through a CompoundCommand (
>> appendAndExecute ). Once the inactivity timeout detected, the
>> CompoundCommand is executed on the Element's CommandStack.
> But at this point you've already done all the things?
Yes absolutely.
>> The issue with this design are :
>> + The last command is executed twice : once with
>> CompoundCommand.appendAndExecute(), and once within
>> commandStack.execute(compoundCmd)
> Yes, I imagine that's the case.
And that's bad...
>> + CompoundCommand can contain 5000 Commands thus creating a lot of
>> unnecessary objects and intermediate states
> Is this an actual performance issue you need to address?
I guess it will in the future because I can have about 400 elements, each one associated with a command stack containing several undo state each could contain thousands of commands.

>> + Maybe I can create an OptimizedCompoundCommand that merges similar
>> Commands to keep the CompoundCommand small ?
>>
>> 3/ Another untested design
>> ==========================
>>
>> -1- Using EMF.Compare
>>
>> This is my last idea so far but I haven't implemented it yet. I plan
>> to make a copy of the Element before the first modification, then as
>> the timeout occurs I plan to use EMF.Compare to track the differences
>> between the first and last state, storing the differences in a Command
>> to add it to the CommandStack.
> I can't imagine that working better than a change recorder.
Ok, thx.
>>
>>
>> 4/ The questions
>> ================
>>
>> Do you think this last design will do ? Do you see any inconvenient
>> with this method ?
> It sounds not so good.
Ok, I'll back up to my first solution so.

>> How can I provide one CommandStack per Element ? for the moment I'm
>> using an AdapterFactoryEditingDomain but only one CommandStack is
>> associated with it so I'm planning to explicitely add a CommandStack
>> reference to my Element class.
> If you want separate undos, you'd definitely need to keep them separate.
Ok.
>> Will it integrates nicely with Copy/Paste commands ?
> It all sounds a little confusing. I'm not sure how, for example, the
> user can control what gets undone when they invoke undo.
>>
>> I'm sorry for this rather long post but I'm running out of ideas right
>> now and I think I need help.
> I'm really not sure what to suggest given the complex design you've
> outlined. Probably looking at ChangeCommand will help, but your
> scenario is tricky because you don't do everything while executing the
> command; you keep doing things and eventually you want to indicate that
> the command is complete and the changes should be rolled up and captured...
For sure, it's not trivial. Anyway many thanks for your insights, it helps a lot.

Best regards,
Guillaume
Re: Implementing Undo properly with multiple EditingDomain and lots of commands [message #502002 is a reply to message #501984] Fri, 04 December 2009 17:16 Go to previous message
Ed Merks is currently offline Ed MerksFriend
Messages: 30620
Registered: July 2009
Senior Member
Guillaume,

Comments below.

Guillaume CHATELET wrote:
> Ed Merks wrote:
>>> I tried to implement undo/redo on my model for a few weeks now but I
>>> can't seem to find the correct approach.
>> The generated editor does this out of the box though...
>
> I should have mentioned I'm not within Eclipse but in a pure Java
> application using EMF. I have complicated GUI issues I can't integrate
> easily within Eclipse.
I see.
>
>>> 2/ The tested designs
>>> =====================
>>>
>>> -1- First approach listening to changes with a ChangeRecorder
>>>
>>> I first tried to adapt my current Element with a ChangeRecorder so
>>> every modification will be tracked and recorded. Once reached the
>>> 500ms of inactivity, this ChangeRecorder is ended and generates a
>>> ChangeDescription. Encapsulating the ChangeDescription in a Command
>>> allowed me to implement the undo/redo.
>>> The issue with this design is : undoing generates a new model
>>> modification leading to a new ChangeDescription appended to my
>>> CommandStack...
>> Are you using a ChangeCommand? While undoing you're clearly need to
>> stop recording changes...
> I tried the ChangeCommand yes and also a custom command ( as I
> discovered ChangeCommand lately )
> Maybe this is my best option right now.
>>> -2- Everything with commands
>>>
>>> I then tried to perform all the Element modifications through
>>> commands. Those commands are gathered through a CompoundCommand (
>>> appendAndExecute ). Once the inactivity timeout detected, the
>>> CompoundCommand is executed on the Element's CommandStack.
>> But at this point you've already done all the things?
> Yes absolutely.
>>> The issue with this design are :
>>> + The last command is executed twice : once with
>>> CompoundCommand.appendAndExecute(), and once within
>>> commandStack.execute(compoundCmd)
>> Yes, I imagine that's the case.
> And that's bad...
>>> + CompoundCommand can contain 5000 Commands thus creating a lot of
>>> unnecessary objects and intermediate states
>> Is this an actual performance issue you need to address?
> I guess it will in the future because I can have about 400 elements,
> each one associated with a command stack containing several undo state
> each could contain thousands of commands.
Suppose each command is 100B, that would be like 4MB, but yes, I think a
change recorder would be more scalable.
>
>>> + Maybe I can create an OptimizedCompoundCommand that merges similar
>>> Commands to keep the CompoundCommand small ?
>>>
>>> 3/ Another untested design
>>> ==========================
>>>
>>> -1- Using EMF.Compare
>>>
>>> This is my last idea so far but I haven't implemented it yet. I plan
>>> to make a copy of the Element before the first modification, then as
>>> the timeout occurs I plan to use EMF.Compare to track the
>>> differences between the first and last state, storing the
>>> differences in a Command to add it to the CommandStack.
>> I can't imagine that working better than a change recorder.
> Ok, thx.
>>>
>>>
>>> 4/ The questions
>>> ================
>>>
>>> Do you think this last design will do ? Do you see any inconvenient
>>> with this method ?
>> It sounds not so good.
> Ok, I'll back up to my first solution so.
>
>>> How can I provide one CommandStack per Element ? for the moment I'm
>>> using an AdapterFactoryEditingDomain but only one CommandStack is
>>> associated with it so I'm planning to explicitely add a CommandStack
>>> reference to my Element class.
>> If you want separate undos, you'd definitely need to keep them separate.
> Ok.
>>> Will it integrates nicely with Copy/Paste commands ?
>> It all sounds a little confusing. I'm not sure how, for example, the
>> user can control what gets undone when they invoke undo.
>>>
>>> I'm sorry for this rather long post but I'm running out of ideas
>>> right now and I think I need help.
>> I'm really not sure what to suggest given the complex design you've
>> outlined. Probably looking at ChangeCommand will help, but your
>> scenario is tricky because you don't do everything while executing
>> the command; you keep doing things and eventually you want to
>> indicate that the command is complete and the changes should be
>> rolled up and captured...
> For sure, it's not trivial. Anyway many thanks for your insights, it
> helps a lot.
>
> Best regards,
> Guillaume
Previous Topic:new EMF tutorial
Next Topic:EAnnotation and DynamicEObjectImpl
Goto Forum:
  


Current Time: Tue Nov 12 10:46:04 GMT 2019

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

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

Back to the top