Bug 209921 - [implementation] UndoableTextChange.canUndo() returns wrong result
Summary: [implementation] UndoableTextChange.canUndo() returns wrong result
Status: CLOSED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: Text (show other bugs)
Version: 3.4   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.4 M4   Edit
Assignee: Platform-Text-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-11-15 06:41 EST by Zhongbo Li CLA
Modified: 2010-10-01 14:42 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Zhongbo Li CLA 2007-11-15 06:41:43 EST
'org.eclipse.text.undo.DocumentUndoManager.UndoableTextChange.canUndo()' returns true when last undo operation runed, and 'undoTextChange()' method is also called.

expect:
      return false
Comment 1 Dani Megert CLA 2007-11-15 06:47:40 EST
Do you have a concrete test case that exhibits the problem?
Comment 2 Dani Megert CLA 2007-11-16 02:28:14 EST
Please reopen when the requested info has been provided.
Comment 3 Zhongbo Li CLA 2007-11-19 01:27:01 EST
please run the testUndoable() method:

--------------------------------------------------------------------

	public void testUndoable(){
		IUndoableOperation operation = new AbstractOperation("test undoable"){

			public IStatus execute(IProgressMonitor monitor, IAdaptable info)
					throws ExecutionException
			{
				return null;
			}

			public IStatus redo(IProgressMonitor monitor, IAdaptable info)
					throws ExecutionException
			{
				return null;
			}

			public IStatus undo(IProgressMonitor monitor, IAdaptable info)
					throws ExecutionException
			{
				return null;
			}
		};

		final DocumentUndoManager undoManager = new DocumentUndoManager(new Document());

		undoManager.addDocumentUndoListener(new IDocumentUndoListener(){

			public void documentUndoNotification(DocumentUndoEvent event){
				if(event.getEventType() == DocumentUndoEvent.ABOUT_TO_UNDO){
					System.out.println("before undo:" + undoManager.undoable());
				}else if(event.getEventType() == DocumentUndoEvent.UNDONE){
					System.out.println("after undo:" + undoManager.undoable());
				}
			}
		});

		undoManager.connect(new Object());
		operation.addContext(undoManager.getUndoContext());
		OperationHistoryFactory.getOperationHistory().add(operation);
		undoManager.transferUndoHistory(undoManager);

		try{
			undoManager.undo();
		}catch(ExecutionException e){
			e.printStackTrace();
		}
}

-------------------------------------------------------------------

I guess 'DocumentUndoManager.undoable()' should returns false when getting a 'DocumentUndoEvent.UNDONE' notification in a DocumentUndoListener. But true is returned now.
Comment 4 Dani Megert CLA 2007-11-19 06:43:26 EST
Your example code is badly broken: you create your own operation which always returns 'true' on canUndo(). Also, it is wrong to transfer an operation history from the same manager to itself.
Comment 5 Zhongbo Li CLA 2007-11-19 12:25:21 EST
I modified this method:

--------------------------------------------------------------------

        public void testUndoable(){
                IUndoableOperation operation = new AbstractOperation("test
undoable"){

                        public IStatus execute(IProgressMonitor monitor,
IAdaptable info)
                                        throws ExecutionException
                        {
                                return null;
                        }

                        public IStatus redo(IProgressMonitor monitor,
IAdaptable info)
                                        throws ExecutionException
                        {
                                return null;
                        }

                        public IStatus undo(IProgressMonitor monitor,
IAdaptable info)
                                        throws ExecutionException
                        {
                                return null;
                        }
                };

                DocumentUndoManager tempUndoManager = new
DocumentUndoManager(new Document());

                tempUndoManager.connect(new Object());
                operation.addContext(tempUndoManager.getUndoContext());
                OperationHistoryFactory.getOperationHistory().add(operation);

                final DocumentUndoManager undoManager = new
DocumentUndoManager(new Document());

                undoManager.addDocumentUndoListener(new
IDocumentUndoListener(){

                        public void documentUndoNotification(DocumentUndoEvent
event){
                                if(event.getEventType() ==
DocumentUndoEvent.ABOUT_TO_UNDO){
                                        System.out.println("When get DocumentUndoEvent.ABOUT_TO_UNDO in DocumentUndoListener, undoable() is: " +
undoManager.undoable());
                                }else if(event.getEventType() ==
DocumentUndoEvent.UNDONE){
                                        System.out.println("When get DocumentUndoEvent.UNDONE in DocumentUndoListener, undoable() is: " +
undoManager.undoable());
                                }
                        }
                });

                undoManager.connect(new Object());
                undoManager.transferUndoHistory(tempUndoManager);

                try{
                        System.out.println("Before undo, undoable() is: " +
undoManager.undoable());
                        undoManager.undo();
                        System.out.println("After undo, undoable() is: " +
undoManager.undoable());
                }catch(ExecutionException e){
                        e.printStackTrace();
                }
}

----------------------------------------------------------

The results:
*********************************************************

     Before undo, undoable() is: true
     When get DocumentUndoEvent.ABOUT_TO_UNDO in DocumentUndoListener, undoable() is: true
     When get DocumentUndoEvent.UNDONE in DocumentUndoListener, undoable() is true
     After undo, undoable() is: false

**********************************************************

1. My own operation is not always
returns 'true' on canUndo(), because undoable() returns false after undo() is called. (see step 4)

2. I create a temp undo manager to transfer an operation history.

I think undoable() should return false when the DocumentUndoEvent.UNDONE is reveived in DocumentUndoListener. But it returns true.



Comment 6 Dani Megert CLA 2007-11-22 05:06:20 EST
>I think undoable() should return false when the DocumentUndoEvent.UNDONE is
>reveived in DocumentUndoListener.
No. As said before, org.eclipse.core.commands.operations.AbstractOperation.canUndo() and only this used by the undo manager to tell whether undo is possible. Your example operation does not override this and hence what we see is expected.

Telling me that your operation would not always return 'true' and then providing a test case that does otherwise is of no help at all.

Just FYI: I quickly replaced your bogus operation class with a correct implementation (org.eclipse.text.undo.DocumentUndoManager.UndoableTextChange) and that's what you get:

Before undo, undoable() is: true
When get DocumentUndoEvent.ABOUT_TO_UNDO in DocumentUndoListener, undoable() is: true
When get DocumentUndoEvent.UNDONE in DocumentUndoListener, undoable() is: true
After undo, undoable() is: false

So, please do not reopen this again.
Comment 7 Dani Megert CLA 2007-11-22 05:09:35 EST
Argh! OK, I see now what you meant.
Comment 8 Zhongbo Li CLA 2007-11-22 05:34:17 EST
Sorry, At last time, I missed the code:

--------------------------------------------------
	public boolean canUndo() {
		return false;
	}
--------------------------------------------------

Thanks & best regards,
~leigh
Comment 9 Dani Megert CLA 2007-11-22 07:16:15 EST
Exactly ;-) That's how I modified the test case already.

One problem happens when transfering the contexts: for internal purpose this is itself registered as (text) operation and seems to have a little problem, especially when the current operation is not a text operation (UndoableTextChange).

In your test case the correct output would be:
  Before undo, undoable() is: 'false'
  After undo, undoable() is: 'false'
As the canUndo() returns false and the operation isn't a text operation.

If we change the test case so that canUndo() returns 'true' and the ops return an OK status instead of illeagal 'null' we should expect:
  Before undo, undoable() is: 'true'
  After undo, undoable() is: 'false'

NOTE: no events will be sent out as the operation wasn't a document change.


Now, we have to look at the case where a document op is on the stack: there we have indeed a bug.
Comment 10 Dani Megert CLA 2007-11-22 12:10:20 EST
Fixed in HEAD.
Available in builds > I20071122-0010.
Comment 11 Zhongbo Li CLA 2007-11-26 00:13:20 EST
Now, UndoableTextChange.canUndo() is right.
Closed the thread.