KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > commands > operations > DefaultOperationHistory


1 /*******************************************************************************
2  * Copyright (c) 2005, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.commands.operations;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Collections JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.Map JavaDoc;
19
20 import org.eclipse.core.commands.ExecutionException;
21 import org.eclipse.core.commands.util.Tracing;
22 import org.eclipse.core.runtime.Assert;
23 import org.eclipse.core.runtime.IAdaptable;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.ISafeRunnable;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.core.runtime.ListenerList;
28 import org.eclipse.core.runtime.OperationCanceledException;
29 import org.eclipse.core.runtime.SafeRunner;
30 import org.eclipse.core.runtime.Status;
31
32 /**
33  * <p>
34  * A base implementation of IOperationHistory that implements a linear undo and
35  * redo model . The most recently added operation is available for undo, and the
36  * most recently undone operation is available for redo.
37  * </p>
38  * <p>
39  * If the operation eligible for undo is not in a state where it can be undone,
40  * then no undo is available. No other operations are considered. Likewise, if
41  * the operation available for redo cannot be redone, then no redo is available.
42  * </p>
43  * <p>
44  * Implementations for the direct undo and redo of a specified operation are
45  * available. If a strict linear undo is to be enforced, than an
46  * IOperationApprover should be installed that prevents undo and redo of any
47  * operation that is not the most recently undone or redone operation in all of
48  * its undo contexts.
49  * </p>
50  * <p>
51  * The data structures used by the DefaultOperationHistory are synchronized, and
52  * entry points that modify the undo and redo history concurrently are also
53  * synchronized. This means that the DefaultOperationHistory is relatively
54  * "thread-friendly" in its implementation. Outbound notifications or operation
55  * approval requests will occur on the thread that initiated the request.
56  * Clients may use DefaultOperationHistory API from any thread; however,
57  * listeners or operation approvers that receive notifications from the
58  * DefaultOperationHistory must be prepared to receive these notifications from
59  * a background thread. Any UI access occurring inside these notifications must
60  * be properly synchronized using the techniques specified by the client's
61  * widget library.
62  * </p>
63  *
64  * <p>
65  * This implementation is not intended to be subclassed.
66  * </p>
67  *
68  * @see org.eclipse.core.commands.operations.IOperationHistory
69  * @see org.eclipse.core.commands.operations.IOperationApprover
70  *
71  * @since 3.1
72  */

73 public final class DefaultOperationHistory implements IOperationHistory {
74     /**
75      * This flag can be set to <code>true</code> if the history should print
76      * information to <code>System.out</code> whenever notifications about
77      * changes to the history occur. This flag should be used for debug purposes
78      * only.
79      */

80     public static boolean DEBUG_OPERATION_HISTORY_NOTIFICATION = false;
81
82     /**
83      * This flag can be set to <code>true</code> if the history should print
84      * information to <code>System.out</code> whenever an unexpected condition
85      * arises. This flag should be used for debug purposes only.
86      */

87     public static boolean DEBUG_OPERATION_HISTORY_UNEXPECTED = false;
88
89     /**
90      * This flag can be set to <code>true</code> if the history should print
91      * information to <code>System.out</code> whenever an undo context is
92      * disposed. This flag should be used for debug purposes only.
93      */

94     public static boolean DEBUG_OPERATION_HISTORY_DISPOSE = false;
95
96     /**
97      * This flag can be set to <code>true</code> if the history should print
98      * information to <code>System.out</code> during the open/close sequence.
99      * This flag should be used for debug purposes only.
100      */

101     public static boolean DEBUG_OPERATION_HISTORY_OPENOPERATION = false;
102
103     /**
104      * This flag can be set to <code>true</code> if the history should print
105      * information to <code>System.out</code> whenever an operation is not
106      * approved. This flag should be used for debug purposes only.
107      */

108     public static boolean DEBUG_OPERATION_HISTORY_APPROVAL = false;
109
110     static final int DEFAULT_LIMIT = 20;
111
112     /**
113      * the list of {@link IOperationApprover}s
114      */

115     ListenerList approvers = new ListenerList(ListenerList.IDENTITY);
116
117     /**
118      * a map of undo limits per context
119      */

120     private Map JavaDoc limits = Collections.synchronizedMap(new HashMap JavaDoc());
121
122     /**
123      * the list of {@link IOperationHistoryListener}s
124      */

125     ListenerList listeners = new ListenerList(ListenerList.IDENTITY);
126
127     /**
128      * the list of operations available for redo, LIFO
129      */

130     private List JavaDoc redoList = Collections.synchronizedList(new ArrayList JavaDoc());
131
132     /**
133      * the list of operations available for undo, LIFO
134      */

135     private List JavaDoc undoList = Collections.synchronizedList(new ArrayList JavaDoc());
136
137     /**
138      * a lock that is used to synchronize access between the undo and redo
139      * history
140      */

141     final Object JavaDoc undoRedoHistoryLock = new Object JavaDoc();
142
143     /**
144      * An operation that is "absorbing" all other operations while it is open.
145      * When this is not null, other operations added or executed are added to
146      * this composite.
147      *
148      */

149     private ICompositeOperation openComposite = null;
150
151     /**
152      * a lock that is used to synchronize access to the open composite.
153      */

154     final Object JavaDoc openCompositeLock = new Object JavaDoc();
155
156     /**
157      * Create an instance of DefaultOperationHistory.
158      */

159     public DefaultOperationHistory() {
160         super();
161     }
162
163     /*
164      * (non-Javadoc)
165      *
166      * @see org.eclipse.core.commands.operations.IOperationHistory#add(org.eclipse.core.commands.operations.IUndoableOperation)
167      */

168     public void add(IUndoableOperation operation) {
169         Assert.isNotNull(operation);
170
171         /*
172          * If we are in the middle of executing an open batching operation, and
173          * this is not that operation, then we need only add the context of the
174          * new operation to the batch. The operation itself is disposed since we
175          * will never undo or redo it. We consider it to be triggered by the
176          * batching operation and assume that its undo will be triggered by the
177          * batching operation undo.
178          */

179         synchronized (openCompositeLock) {
180             if (openComposite != null && openComposite != operation) {
181                 openComposite.add(operation);
182                 return;
183             }
184         }
185
186         if (checkUndoLimit(operation)) {
187             synchronized (undoRedoHistoryLock) {
188                 undoList.add(operation);
189             }
190             notifyAdd(operation);
191
192             // flush redo stack for related contexts
193
IUndoContext[] contexts = operation.getContexts();
194             for (int i = 0; i < contexts.length; i++) {
195                 flushRedo(contexts[i]);
196             }
197         } else {
198             // Dispose the operation since we will not have a reference to it.
199
operation.dispose();
200         }
201     }
202
203     /**
204      * <p>
205      * Add the specified approver to the list of operation approvers consulted
206      * by the operation history before an undo or redo is allowed to proceed.
207      * This method has no effect if the instance being added is already in the
208      * list.
209      * </p>
210      * <p>
211      * Operation approvers must be prepared to receive these the operation
212      * approval messages from a background thread. Any UI access occurring
213      * inside the implementation must be properly synchronized using the
214      * techniques specified by the client's widget library.
215      * </p>
216      *
217      * @param approver
218      * the IOperationApprover to be added as an approver.
219      *
220      */

221
222     public void addOperationApprover(IOperationApprover approver) {
223         approvers.add(approver);
224     }
225
226     /**
227      * <p>
228      * Add the specified listener to the list of operation history listeners
229      * that are notified about changes in the history or operations that are
230      * executed, undone, or redone. This method has no effect if the instance
231      * being added is already in the list.
232      * </p>
233      * <p>
234      * Operation history listeners must be prepared to receive notifications
235      * from a background thread. Any UI access occurring inside the
236      * implementation must be properly synchronized using the techniques
237      * specified by the client's widget library.
238      * </p>
239      *
240      * @param listener
241      * the IOperationHistoryListener to be added as a listener.
242      *
243      * @see org.eclipse.core.commands.operations.IOperationHistoryListener
244      * @see org.eclipse.core.commands.operations.OperationHistoryEvent
245      */

246     public void addOperationHistoryListener(IOperationHistoryListener listener) {
247         listeners.add(listener);
248     }
249
250     /*
251      * (non-Javadoc)
252      *
253      * @see org.eclipse.core.commands.operations.IOperationHistory#canRedo(org.eclipse.core.commands.operations.IUndoContext)
254      */

255     public boolean canRedo(IUndoContext context) {
256         // null context is allowed and passed through
257
IUndoableOperation operation = getRedoOperation(context);
258         return (operation != null && operation.canRedo());
259     }
260
261     /*
262      * (non-Javadoc)
263      *
264      * @see org.eclipse.core.commands.operations.IOperationHistory#canUndo(org.eclipse.core.commands.operations.IUndoContext)
265      */

266     public boolean canUndo(IUndoContext context) {
267         // null context is allowed and passed through
268
IUndoableOperation operation = getUndoOperation(context);
269         return (operation != null && operation.canUndo());
270     }
271
272     /**
273      * Check the redo limit before adding an operation. In theory the redo limit
274      * should never be reached, because the redo items are transferred from the
275      * undo history, which has the same limit. The redo history is cleared
276      * whenever a new operation is added. We check for completeness since
277      * implementations may change over time.
278      *
279      * Return a boolean indicating whether the redo should proceed.
280      */

281     private boolean checkRedoLimit(IUndoableOperation operation) {
282         IUndoContext[] contexts = operation.getContexts();
283         for (int i = 0; i < contexts.length; i++) {
284             int limit = getLimit(contexts[i]);
285             if (limit > 0) {
286                 forceRedoLimit(contexts[i], limit - 1);
287             } else {
288                 // this context has a 0 limit
289
operation.removeContext(contexts[i]);
290             }
291         }
292         return operation.getContexts().length > 0;
293     }
294
295     /**
296      * Check the undo limit before adding an operation. Return a boolean
297      * indicating whether the undo should proceed.
298      */

299     private boolean checkUndoLimit(IUndoableOperation operation) {
300         IUndoContext[] contexts = operation.getContexts();
301         for (int i = 0; i < contexts.length; i++) {
302             int limit = getLimit(contexts[i]);
303             if (limit > 0) {
304                 forceUndoLimit(contexts[i], limit - 1);
305             } else {
306                 // this context has a 0 limit
307
operation.removeContext(contexts[i]);
308             }
309         }
310         return operation.getContexts().length > 0;
311     }
312
313     /*
314      * (non-Javadoc)
315      *
316      * @see org.eclipse.core.commands.operations.IOperationHistory#dispose(org.eclipse.core.commands.operations.IUndoContext,
317      * boolean, boolean, boolean)
318      */

319     public void dispose(IUndoContext context, boolean flushUndo,
320             boolean flushRedo, boolean flushContext) {
321         // dispose of any limit that was set for the context if it is not to be
322
// used again.
323
if (flushContext) {
324             if (DEBUG_OPERATION_HISTORY_DISPOSE) {
325                 Tracing.printTrace("OPERATIONHISTORY", "Flushing context " //$NON-NLS-1$//$NON-NLS-2$
326
+ context);
327             }
328             flushUndo(context);
329             flushRedo(context);
330             limits.remove(context);
331             return;
332         }
333         if (flushUndo) {
334             flushUndo(context);
335         }
336         if (flushRedo) {
337             flushRedo(context);
338         }
339
340     }
341
342     /**
343      * Perform the redo. All validity checks have already occurred.
344      *
345      * @param monitor
346      * @param operation
347      */

348     private IStatus doRedo(IProgressMonitor monitor, IAdaptable info,
349             IUndoableOperation operation) throws ExecutionException {
350
351         IStatus status = getRedoApproval(operation, info);
352         if (status.isOK()) {
353             notifyAboutToRedo(operation);
354             try {
355                 status = operation.redo(monitor, info);
356             } catch (OperationCanceledException e) {
357                 status = Status.CANCEL_STATUS;
358             } catch (ExecutionException e) {
359                 notifyNotOK(operation);
360                 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
361                     Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
362
"ExecutionException while redoing " + operation); //$NON-NLS-1$
363
}
364                 throw e;
365             } catch (Exception JavaDoc e) {
366                 notifyNotOK(operation);
367                 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
368                     Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
369
"Exception while redoing " + operation); //$NON-NLS-1$
370
}
371                 throw new ExecutionException(
372                         "While redoing the operation, an exception occurred", e); //$NON-NLS-1$
373
}
374         }
375
376         // if successful, the operation is removed from the redo history and
377
// placed back in the undo history.
378
if (status.isOK()) {
379             boolean addedToUndo = true;
380             synchronized (undoRedoHistoryLock) {
381                 redoList.remove(operation);
382                 if (checkUndoLimit(operation)) {
383                     undoList.add(operation);
384                 } else {
385                     addedToUndo = false;
386                 }
387             }
388             // dispose the operation since we could not add it to the
389
// stack and will no longer have a reference to it.
390
if (!addedToUndo) {
391                 operation.dispose();
392             }
393
394             // notify listeners must happen after history is updated
395
notifyRedone(operation);
396         } else {
397             notifyNotOK(operation, status);
398         }
399
400         return status;
401     }
402
403     /**
404      * Perform the undo. All validity checks have already occurred.
405      *
406      * @param monitor
407      * @param operation
408      */

409     private IStatus doUndo(IProgressMonitor monitor, IAdaptable info,
410             IUndoableOperation operation) throws ExecutionException {
411         IStatus status = getUndoApproval(operation, info);
412         if (status.isOK()) {
413             notifyAboutToUndo(operation);
414             try {
415                 status = operation.undo(monitor, info);
416             } catch (OperationCanceledException e) {
417                 status = Status.CANCEL_STATUS;
418             } catch (ExecutionException e) {
419                 notifyNotOK(operation);
420                 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
421                     Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
422
"ExecutionException while undoing " + operation); //$NON-NLS-1$
423
}
424                 throw e;
425             } catch (Exception JavaDoc e) {
426                 notifyNotOK(operation);
427                 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
428                     Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
429
"Exception while undoing " + operation); //$NON-NLS-1$
430
}
431                 throw new ExecutionException(
432                         "While undoing the operation, an exception occurred", e); //$NON-NLS-1$
433
}
434         }
435         // if successful, the operation is removed from the undo history and
436
// placed in the redo history.
437
if (status.isOK()) {
438             boolean addedToRedo = true;
439             synchronized (undoRedoHistoryLock) {
440                 undoList.remove(operation);
441                 if (checkRedoLimit(operation)) {
442                     redoList.add(operation);
443                 } else {
444                     addedToRedo = false;
445                 }
446             }
447             // dispose the operation since we could not add it to the
448
// stack and will no longer have a reference to it.
449
if (!addedToRedo) {
450                 operation.dispose();
451             }
452             // notification occurs after the undo and redo histories are
453
// adjusted
454
notifyUndone(operation);
455         } else {
456             notifyNotOK(operation, status);
457         }
458         return status;
459     }
460
461     /*
462      * (non-Javadoc)
463      *
464      * @see org.eclipse.core.commands.operations.IOperationHistory#execute(org.eclipse.core.commands.operations.IUndoableOperation,
465      * org.eclipse.core.runtime.IProgressMonitor,
466      * org.eclipse.core.runtime.IAdaptable)
467      */

468     public IStatus execute(IUndoableOperation operation,
469             IProgressMonitor monitor, IAdaptable info)
470             throws ExecutionException {
471         Assert.isNotNull(operation);
472
473         // error if operation is invalid
474
if (!operation.canExecute()) {
475             return IOperationHistory.OPERATION_INVALID_STATUS;
476         }
477
478         // check with the operation approvers
479
IStatus status = getExecuteApproval(operation, info);
480         if (!status.isOK()) {
481             // not approved. No notifications are sent, just return the status.
482
return status;
483         }
484
485         /*
486          * If we are in the middle of an open composite, then we will add this
487          * operation to the open operation rather than add the operation to the
488          * history. We will still execute it.
489          */

490         boolean merging = false;
491         synchronized (openCompositeLock) {
492             if (openComposite != null) {
493                 // the composite shouldn't be executed explicitly while it is
494
// still
495
// open
496
if (openComposite == operation) {
497                     return IOperationHistory.OPERATION_INVALID_STATUS;
498                 }
499                 openComposite.add(operation);
500                 merging = true;
501             }
502         }
503
504         /*
505          * Execute the operation
506          */

507         if (!merging) {
508             notifyAboutToExecute(operation);
509         }
510         try {
511             status = operation.execute(monitor, info);
512         } catch (OperationCanceledException e) {
513             status = Status.CANCEL_STATUS;
514         } catch (ExecutionException e) {
515             notifyNotOK(operation);
516             throw e;
517         } catch (Exception JavaDoc e) {
518             notifyNotOK(operation);
519             throw new ExecutionException(
520                     "While executing the operation, an exception occurred", e); //$NON-NLS-1$
521
}
522
523         // if successful, the notify listeners are notified and the operation is
524
// added to the history
525
if (!merging) {
526             if (status.isOK()) {
527                 notifyDone(operation);
528                 add(operation);
529             } else {
530                 notifyNotOK(operation, status);
531                 // dispose the operation since we did not add it to the stack
532
// and will no longer have a reference to it.
533
operation.dispose();
534             }
535         }
536         // all other severities are not interpreted. Simply return the status.
537
return status;
538     }
539
540     /*
541      * Filter the specified list to include only the specified undo context.
542      */

543     private IUndoableOperation[] filter(List JavaDoc list, IUndoContext context) {
544         /*
545          * This method is used whenever there is a need to filter the undo or
546          * redo history on a particular context. Currently there are no caches
547          * kept to optimize repeated requests for the same filter. If benchmarks
548          * show this to be a common pattern that causes performances problems,
549          * we could implement a filtered cache here that is nullified whenever
550          * the global history changes.
551          */

552
553         List JavaDoc filtered = new ArrayList JavaDoc();
554         Iterator JavaDoc iterator = list.iterator();
555         synchronized (undoRedoHistoryLock) {
556             while (iterator.hasNext()) {
557                 IUndoableOperation operation = (IUndoableOperation) iterator
558                         .next();
559                 if (operation.hasContext(context)) {
560                     filtered.add(operation);
561                 }
562             }
563         }
564         return (IUndoableOperation[]) filtered
565                 .toArray(new IUndoableOperation[filtered.size()]);
566     }
567
568     /*
569      * Flush the redo stack of all operations that have the given context.
570      */

571     private void flushRedo(IUndoContext context) {
572         if (DEBUG_OPERATION_HISTORY_DISPOSE) {
573             Tracing.printTrace("OPERATIONHISTORY", "Flushing redo history for " //$NON-NLS-1$ //$NON-NLS-2$
574
+ context);
575         }
576
577         Object JavaDoc[] filtered = filter(redoList, context);
578         for (int i = 0; i < filtered.length; i++) {
579             IUndoableOperation operation = (IUndoableOperation) filtered[i];
580             if (context == GLOBAL_UNDO_CONTEXT
581                     || operation.getContexts().length == 1) {
582                 // remove the operation if it only has the context or we are
583
// flushing all
584
redoList.remove(operation);
585                 internalRemove(operation);
586             } else {
587                 // remove the reference to the context.
588
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=161786
589
// It is not enough to simply remove the context. There could
590
// be one or more contexts that match the one we are trying to
591
// dispose.
592
IUndoContext[] contexts = operation.getContexts();
593                 for (int j = 0; j < contexts.length; j++) {
594                     if (contexts[j].matches(context)) {
595                         operation.removeContext(contexts[j]);
596                     }
597                 }
598                 if (operation.getContexts().length == 0) {
599                     redoList.remove(operation);
600                     internalRemove(operation);
601                 }
602             }
603         }
604     }
605
606     /*
607      * Flush the undo stack of all operations that have the given context.
608      */

609     private void flushUndo(IUndoContext context) {
610         if (DEBUG_OPERATION_HISTORY_DISPOSE) {
611             Tracing.printTrace("OPERATIONHISTORY", "Flushing undo history for " //$NON-NLS-1$ //$NON-NLS-2$
612
+ context);
613         }
614
615         // Get all operations that have the context (or one that matches)
616
Object JavaDoc[] filtered = filter(undoList, context);
617         for (int i = 0; i < filtered.length; i++) {
618             IUndoableOperation operation = (IUndoableOperation) filtered[i];
619             if (context == GLOBAL_UNDO_CONTEXT
620                     || operation.getContexts().length == 1) {
621                 // remove the operation if it only has the context or we are
622
// flushing all
623
undoList.remove(operation);
624                 internalRemove(operation);
625             } else {
626                 // remove the reference to the context.
627
// See https://bugs.eclipse.org/bugs/show_bug.cgi?id=161786
628
// It is not enough to simply remove the context. There could
629
// be one or more contexts that match the one we are trying to
630
// dispose.
631
IUndoContext[] contexts = operation.getContexts();
632                 for (int j = 0; j < contexts.length; j++) {
633                     if (contexts[j].matches(context)) {
634                         operation.removeContext(contexts[j]);
635                     }
636                 }
637                 if (operation.getContexts().length == 0) {
638                     undoList.remove(operation);
639                     internalRemove(operation);
640                 }
641             }
642         }
643         /*
644          * There may be an open composite. If it has this context, then the
645          * context must be removed. If it has only this context or we are
646          * flushing all operations, then null it out and notify that we are
647          * ending it. We don't remove it since it was never added.
648          */

649         ICompositeOperation endedComposite = null;
650         synchronized (openCompositeLock) {
651             if (openComposite != null) {
652                 if (openComposite.hasContext(context)) {
653                     if (context == GLOBAL_UNDO_CONTEXT
654                             || openComposite.getContexts().length == 1) {
655                         endedComposite = openComposite;
656                         openComposite = null;
657                     } else {
658                         openComposite.removeContext(context);
659                     }
660                 }
661             }
662         }
663         // notify outside of the synchronized block.
664
if (endedComposite != null) {
665             notifyNotOK(endedComposite);
666         }
667     }
668
669     /*
670      * Force the redo history for the given context to contain max or less
671      * items.
672      */

673     private void forceRedoLimit(IUndoContext context, int max) {
674         Object JavaDoc[] filtered = filter(redoList, context);
675         int size = filtered.length;
676         if (size > 0) {
677             int index = 0;
678             while (size > max) {
679                 IUndoableOperation removed = (IUndoableOperation) filtered[index];
680                 if (context == GLOBAL_UNDO_CONTEXT
681                         || removed.getContexts().length == 1) {
682                     /*
683                      * remove the operation if we are enforcing a global limit
684                      * or if the operation only has the specified context
685                      */

686                     redoList.remove(removed);
687                     internalRemove(removed);
688                 } else {
689                     /*
690                      * if the operation has multiple contexts and we've reached
691                      * the limit for only one of them, then just remove the
692                      * context, not the operation.
693                      */

694                     removed.removeContext(context);
695                 }
696                 size--;
697                 index++;
698             }
699         }
700     }
701
702     /*
703      * Force the undo history for the given context to contain max or less
704      * items.
705      */

706     private void forceUndoLimit(IUndoContext context, int max) {
707         Object JavaDoc[] filtered = filter(undoList, context);
708         int size = filtered.length;
709         if (size > 0) {
710             int index = 0;
711             while (size > max) {
712                 IUndoableOperation removed = (IUndoableOperation) filtered[index];
713                 if (context == GLOBAL_UNDO_CONTEXT
714                         || removed.getContexts().length == 1) {
715                     /*
716                      * remove the operation if we are enforcing a global limit
717                      * or if the operation only has the specified context
718                      */

719                     undoList.remove(removed);
720                     internalRemove(removed);
721                 } else {
722                     /*
723                      * if the operation has multiple contexts and we've reached
724                      * the limit for only one of them, then just remove the
725                      * context, not the operation.
726                      */

727                     removed.removeContext(context);
728                 }
729                 size--;
730                 index++;
731             }
732         }
733     }
734
735     /*
736      * (non-Javadoc)
737      *
738      * @see org.eclipse.core.commands.operations.IOperationHistory#getLimit()
739      */

740     public int getLimit(IUndoContext context) {
741         if (!limits.containsKey(context)) {
742             return DEFAULT_LIMIT;
743         }
744         return ((Integer JavaDoc) (limits.get(context))).intValue();
745     }
746
747     /*
748      * Consult the IOperationApprovers to see if the proposed redo should be
749      * allowed.
750      */

751     private IStatus getRedoApproval(IUndoableOperation operation,
752             IAdaptable info) {
753
754         final Object JavaDoc[] approverArray = approvers.getListeners();
755
756         for (int i = 0; i < approverArray.length; i++) {
757             IOperationApprover approver = (IOperationApprover) approverArray[i];
758             IStatus approval = approver.proceedRedoing(operation, this, info);
759             if (!approval.isOK()) {
760                 if (DEBUG_OPERATION_HISTORY_APPROVAL) {
761                     Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
762
"Redo not approved by " + approver //$NON-NLS-1$
763
+ "for operation " + operation //$NON-NLS-1$
764
+ " approved by " + approval); //$NON-NLS-1$
765
}
766                 return approval;
767             }
768         }
769         return Status.OK_STATUS;
770     }
771
772     /*
773      * (non-Javadoc)
774      *
775      * @see org.eclipse.core.commands.operations.IOperationHistory#getRedoHistory(org.eclipse.core.commands.operations.IUndoContext)
776      */

777     public IUndoableOperation[] getRedoHistory(IUndoContext context) {
778         Assert.isNotNull(context);
779         return filter(redoList, context);
780     }
781
782     /*
783      * (non-Javadoc)
784      *
785      * @see org.eclipse.core.commands.operations.IOperationHistory#getOperation(org.eclipse.core.commands.operations.IUndoContext)
786      */

787     public IUndoableOperation getRedoOperation(IUndoContext context) {
788         Assert.isNotNull(context);
789         synchronized (undoRedoHistoryLock) {
790             for (int i = redoList.size() - 1; i >= 0; i--) {
791                 IUndoableOperation operation = (IUndoableOperation) redoList
792                         .get(i);
793                 if (operation.hasContext(context)) {
794                     return operation;
795                 }
796             }
797         }
798         return null;
799     }
800
801     /*
802      * Consult the IOperationApprovers to see if the proposed undo should be
803      * allowed.
804      */

805     private IStatus getUndoApproval(IUndoableOperation operation,
806             IAdaptable info) {
807
808         final Object JavaDoc[] approverArray = approvers.getListeners();
809
810         for (int i = 0; i < approverArray.length; i++) {
811             IOperationApprover approver = (IOperationApprover) approverArray[i];
812             IStatus approval = approver.proceedUndoing(operation, this, info);
813             if (!approval.isOK()) {
814                 if (DEBUG_OPERATION_HISTORY_APPROVAL) {
815                     Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
816
"Undo not approved by " + approver //$NON-NLS-1$
817
+ "for operation " + operation //$NON-NLS-1$
818
+ " with status " + approval); //$NON-NLS-1$
819
}
820                 return approval;
821             }
822         }
823         return Status.OK_STATUS;
824     }
825
826     /*
827      * (non-Javadoc)
828      *
829      * @see org.eclipse.core.commands.operations.IOperationHistory#getUndoHistory(org.eclipse.core.commands.operations.IUndoContext)
830      */

831     public IUndoableOperation[] getUndoHistory(IUndoContext context) {
832         Assert.isNotNull(context);
833         return filter(undoList, context);
834     }
835
836     /*
837      * (non-Javadoc)
838      *
839      * @see org.eclipse.core.commands.operations.IOperationHistory#getUndoOperation(org.eclipse.core.commands.operations.IUndoContext)
840      */

841     public IUndoableOperation getUndoOperation(IUndoContext context) {
842         Assert.isNotNull(context);
843         synchronized (undoRedoHistoryLock) {
844             for (int i = undoList.size() - 1; i >= 0; i--) {
845                 IUndoableOperation operation = (IUndoableOperation) undoList
846                         .get(i);
847                 if (operation.hasContext(context)) {
848                     return operation;
849                 }
850             }
851         }
852         return null;
853     }
854
855     /*
856      * Consult the IOperationApprovers to see if the proposed execution should
857      * be allowed.
858      *
859      * @since 3.2
860      */

861     private IStatus getExecuteApproval(IUndoableOperation operation,
862             IAdaptable info) {
863
864         final Object JavaDoc[] approverArray = approvers.getListeners();
865
866         for (int i = 0; i < approverArray.length; i++) {
867             if (approverArray[i] instanceof IOperationApprover2) {
868                 IOperationApprover2 approver = (IOperationApprover2) approverArray[i];
869                 IStatus approval = approver.proceedExecuting(operation, this,
870                         info);
871                 if (!approval.isOK()) {
872                     if (DEBUG_OPERATION_HISTORY_APPROVAL) {
873                         Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
874
"Execute not approved by " + approver //$NON-NLS-1$
875
+ "for operation " + operation //$NON-NLS-1$
876
+ " with status " + approval); //$NON-NLS-1$
877
}
878                     return approval;
879                 }
880             }
881         }
882         return Status.OK_STATUS;
883     }
884
885     /*
886      * Remove the operation by disposing it and notifying listeners.
887      */

888     private void internalRemove(IUndoableOperation operation) {
889         operation.dispose();
890         notifyRemoved(operation);
891     }
892
893     /*
894      * Notify listeners of an operation event.
895      */

896     private void notifyListeners(final OperationHistoryEvent event) {
897         if (event.getOperation() instanceof IAdvancedUndoableOperation) {
898             final IAdvancedUndoableOperation advancedOp = (IAdvancedUndoableOperation) event
899                     .getOperation();
900             SafeRunner.run(new ISafeRunnable() {
901                 public void handleException(Throwable JavaDoc exception) {
902                     if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
903                         Tracing
904                                 .printTrace(
905                                         "OPERATIONHISTORY", //$NON-NLS-1$
906
"Exception during notification callback " + exception); //$NON-NLS-1$
907
}
908                 }
909
910                 public void run() throws Exception JavaDoc {
911                     advancedOp.aboutToNotify(event);
912                 }
913             });
914         }
915         final Object JavaDoc[] listenerArray = listeners.getListeners();
916         for (int i = 0; i < listenerArray.length; i++) {
917             final IOperationHistoryListener listener = (IOperationHistoryListener) listenerArray[i];
918             SafeRunner.run(new ISafeRunnable() {
919                 public void handleException(Throwable JavaDoc exception) {
920                     if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
921                         Tracing
922                                 .printTrace(
923                                         "OPERATIONHISTORY", //$NON-NLS-1$
924
"Exception during notification callback " + exception); //$NON-NLS-1$
925
}
926                 }
927
928                 public void run() throws Exception JavaDoc {
929                     listener.historyNotification(event);
930                 }
931             });
932         }
933     }
934
935     private void notifyAboutToExecute(IUndoableOperation operation) {
936         if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
937             Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_EXECUTE " //$NON-NLS-1$ //$NON-NLS-2$
938
+ operation);
939         }
940
941         notifyListeners(new OperationHistoryEvent(
942                 OperationHistoryEvent.ABOUT_TO_EXECUTE, this, operation));
943     }
944
945     /*
946      * Notify listeners that an operation is about to redo.
947      */

948     private void notifyAboutToRedo(IUndoableOperation operation) {
949         if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
950             Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_REDO " //$NON-NLS-1$ //$NON-NLS-2$
951
+ operation);
952         }
953
954         notifyListeners(new OperationHistoryEvent(
955                 OperationHistoryEvent.ABOUT_TO_REDO, this, operation));
956     }
957
958     /*
959      * Notify listeners that an operation is about to undo.
960      */

961     private void notifyAboutToUndo(IUndoableOperation operation) {
962         if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
963             Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_UNDO " //$NON-NLS-1$ //$NON-NLS-2$
964
+ operation);
965         }
966
967         notifyListeners(new OperationHistoryEvent(
968                 OperationHistoryEvent.ABOUT_TO_UNDO, this, operation));
969     }
970
971     /*
972      * Notify listeners that an operation has been added.
973      */

974     private void notifyAdd(IUndoableOperation operation) {
975         if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
976             Tracing.printTrace("OPERATIONHISTORY", "OPERATION_ADDED " //$NON-NLS-1$ //$NON-NLS-2$
977
+ operation);
978         }
979
980         notifyListeners(new OperationHistoryEvent(
981                 OperationHistoryEvent.OPERATION_ADDED, this, operation));
982     }
983
984     /*
985      * Notify listeners that an operation is done executing.
986      */

987     private void notifyDone(IUndoableOperation operation) {
988         if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
989             Tracing.printTrace("OPERATIONHISTORY", "DONE " + operation); //$NON-NLS-1$ //$NON-NLS-2$
990
}
991
992         notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.DONE,
993                 this, operation));
994     }
995
996     /*
997      * Notify listeners that an operation did not succeed after an attempt to
998      * execute, undo, or redo was made.
999      */

1000    private void notifyNotOK(IUndoableOperation operation) {
1001        notifyNotOK(operation, null);
1002    }
1003
1004    /*
1005     * Notify listeners that an operation did not succeed after an attempt to
1006     * execute, undo, or redo was made. Include the status associated with the
1007     * attempt.
1008     *
1009     * @since 3.2
1010     */

1011    private void notifyNotOK(IUndoableOperation operation, IStatus status) {
1012        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
1013            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_NOT_OK " //$NON-NLS-1$ //$NON-NLS-2$
1014
+ operation);
1015        }
1016
1017        notifyListeners(new OperationHistoryEvent(
1018                OperationHistoryEvent.OPERATION_NOT_OK, this, operation, status));
1019    }
1020
1021    /*
1022     * Notify listeners that an operation was redone.
1023     */

1024    private void notifyRedone(IUndoableOperation operation) {
1025        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
1026            Tracing.printTrace("OPERATIONHISTORY", "REDONE " + operation); //$NON-NLS-1$ //$NON-NLS-2$
1027
}
1028
1029        notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.REDONE,
1030                this, operation));
1031    }
1032
1033    /*
1034     * Notify listeners that an operation has been removed from the history.
1035     */

1036    private void notifyRemoved(IUndoableOperation operation) {
1037        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
1038            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_REMOVED " //$NON-NLS-1$ //$NON-NLS-2$
1039
+ operation);
1040        }
1041
1042        notifyListeners(new OperationHistoryEvent(
1043                OperationHistoryEvent.OPERATION_REMOVED, this, operation));
1044    }
1045
1046    /*
1047     * Notify listeners that an operation has been undone.
1048     */

1049    private void notifyUndone(IUndoableOperation operation) {
1050        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
1051            Tracing.printTrace("OPERATIONHISTORY", "UNDONE " + operation); //$NON-NLS-1$ //$NON-NLS-2$
1052
}
1053
1054        notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.UNDONE,
1055                this, operation));
1056    }
1057
1058    /*
1059     * Notify listeners that an operation has been undone.
1060     */

1061    private void notifyChanged(IUndoableOperation operation) {
1062        if (DEBUG_OPERATION_HISTORY_NOTIFICATION) {
1063            Tracing.printTrace("OPERATIONHISTORY", "OPERATION_CHANGED " //$NON-NLS-1$//$NON-NLS-2$
1064
+ operation);
1065        }
1066
1067        notifyListeners(new OperationHistoryEvent(
1068                OperationHistoryEvent.OPERATION_CHANGED, this, operation));
1069    }
1070
1071    /*
1072     * (non-Javadoc)
1073     *
1074     * @see org.eclipse.core.commands.operations.IOperationHistory#redo(org.eclipse.core.commands.operations.IUndoContext,
1075     * org.eclipse.core.runtime.IProgressMonitor,
1076     * org.eclipse.core.runtime.IAdaptable)
1077     */

1078    public IStatus redo(IUndoContext context, IProgressMonitor monitor,
1079            IAdaptable info) throws ExecutionException {
1080        Assert.isNotNull(context);
1081        IUndoableOperation operation = getRedoOperation(context);
1082
1083        // info if there is no operation
1084
if (operation == null) {
1085            return IOperationHistory.NOTHING_TO_REDO_STATUS;
1086        }
1087
1088        // error if operation is invalid
1089
if (!operation.canRedo()) {
1090            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
1091                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
1092
"Redo operation not valid - " + operation); //$NON-NLS-1$
1093
}
1094
1095            return IOperationHistory.OPERATION_INVALID_STATUS;
1096        }
1097
1098        return doRedo(monitor, info, operation);
1099    }
1100
1101    /*
1102     * (non-Javadoc)
1103     *
1104     * @see org.eclipse.core.commands.operations.IOperationHistory#redoOperation(org.eclipse.core.commands.operations.IUndoableOperation,
1105     * org.eclipse.core.runtime.IProgressMonitor,
1106     * org.eclipse.core.runtime.IAdaptable)
1107     */

1108
1109    public IStatus redoOperation(IUndoableOperation operation,
1110            IProgressMonitor monitor, IAdaptable info)
1111            throws ExecutionException {
1112        Assert.isNotNull(operation);
1113        IStatus status;
1114        if (operation.canRedo()) {
1115            status = doRedo(monitor, info, operation);
1116        } else {
1117            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
1118                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
1119
"Redo operation not valid - " + operation); //$NON-NLS-1$
1120
}
1121            status = IOperationHistory.OPERATION_INVALID_STATUS;
1122        }
1123        return status;
1124    }
1125
1126    /*
1127     * (non-Javadoc)
1128     *
1129     * @see org.eclipse.core.commands.operations.IOperationHistory#removeOperationApprover(org.eclipse.core.commands.operations.IOperationApprover)
1130     */

1131    public void removeOperationApprover(IOperationApprover approver) {
1132        approvers.remove(approver);
1133    }
1134
1135    /*
1136     * (non-Javadoc)
1137     *
1138     * @see org.eclipse.core.commands.operations.IOperationHistory#removeOperationHistoryListener(org.eclipse.core.commands.operations.IOperationHistoryListener)
1139     */

1140    public void removeOperationHistoryListener(
1141            IOperationHistoryListener listener) {
1142        listeners.remove(listener);
1143    }
1144
1145    /*
1146     * (non-Javadoc)
1147     *
1148     * @see org.eclipse.core.commands.operations.IOperationHistory#replaceOperation(org.eclipse.core.commands.operations.IUndoableOperation,
1149     * org.eclipse.core.commands.operations.IUndoableOperation [])
1150     */

1151    public void replaceOperation(IUndoableOperation operation,
1152            IUndoableOperation[] replacements) {
1153        // check the undo history first.
1154
boolean inUndo = false;
1155        synchronized (undoRedoHistoryLock) {
1156            int index = undoList.indexOf(operation);
1157            if (index > -1) {
1158                inUndo = true;
1159                undoList.remove(operation);
1160                // notify listeners after the lock on undoList is released
1161
ArrayList JavaDoc allContexts = new ArrayList JavaDoc(replacements.length);
1162                for (int i = 0; i < replacements.length; i++) {
1163                    IUndoContext[] opContexts = replacements[i].getContexts();
1164                    for (int j = 0; j < opContexts.length; j++) {
1165                        allContexts.add(opContexts[j]);
1166                    }
1167                    undoList.add(index, replacements[i]);
1168                    // notify listeners after the lock on the history is
1169
// released
1170
}
1171                // recheck all the limits. We do this at the end so the index
1172
// doesn't change during replacement
1173
for (int i = 0; i < allContexts.size(); i++) {
1174                    IUndoContext context = (IUndoContext) allContexts.get(i);
1175                    forceUndoLimit(context, getLimit(context));
1176                }
1177            }
1178        }
1179        if (inUndo) {
1180            // notify listeners of operations added and removed
1181
internalRemove(operation);
1182            for (int i = 0; i < replacements.length; i++) {
1183                notifyAdd(replacements[i]);
1184            }
1185            return;
1186        }
1187
1188        // operation was not in the undo history. Check the redo history.
1189

1190        synchronized (undoRedoHistoryLock) {
1191            int index = redoList.indexOf(operation);
1192            if (index == -1) {
1193                return;
1194            }
1195            ArrayList JavaDoc allContexts = new ArrayList JavaDoc(replacements.length);
1196            redoList.remove(operation);
1197            // notify listeners after we release the lock on redoList
1198
for (int i = 0; i < replacements.length; i++) {
1199                IUndoContext[] opContexts = replacements[i].getContexts();
1200                for (int j = 0; j < opContexts.length; j++) {
1201                    allContexts.add(opContexts[j]);
1202                }
1203                redoList.add(index, replacements[i]);
1204                // notify listeners after we release the lock on redoList
1205
}
1206            // recheck all the limits. We do this at the end so the index
1207
// doesn't change during replacement
1208
for (int i = 0; i < allContexts.size(); i++) {
1209                IUndoContext context = (IUndoContext) allContexts.get(i);
1210                forceRedoLimit(context, getLimit(context));
1211            }
1212        }
1213        // send listener notifications after we release the lock on the history
1214
internalRemove(operation);
1215        for (int i = 0; i < replacements.length; i++) {
1216            notifyAdd(replacements[i]);
1217        }
1218    }
1219
1220    /*
1221     * (non-Javadoc)
1222     *
1223     * @see org.eclipse.core.commands.operations.IOperationHistory#setLimit(org.eclipse.core.commands.operations.IUndoContext,
1224     * int)
1225     */

1226    public void setLimit(IUndoContext context, int limit) {
1227        Assert.isTrue(limit >= 0);
1228        /*
1229         * The limit checking methods interpret a null context as a global limit
1230         * to be enforced. We do not wish to support a global limit in this
1231         * implementation, so we throw an exception for a null context. The rest
1232         * of the implementation can handle a null context, so subclasses can
1233         * override this if a global limit is desired.
1234         */

1235        Assert.isNotNull(context);
1236        limits.put(context, new Integer JavaDoc(limit));
1237        forceUndoLimit(context, limit);
1238        forceRedoLimit(context, limit);
1239
1240    }
1241
1242    /*
1243     * (non-Javadoc)
1244     *
1245     * @see org.eclipse.core.commands.operations.IOperationHistory#undo(org.eclipse.core.commands.operations.IUndoContext,
1246     * org.eclipse.core.runtime.IProgressMonitor,
1247     * org.eclipse.core.runtime.IAdaptable)
1248     */

1249    public IStatus undo(IUndoContext context, IProgressMonitor monitor,
1250            IAdaptable info) throws ExecutionException {
1251        Assert.isNotNull(context);
1252        IUndoableOperation operation = getUndoOperation(context);
1253
1254        // info if there is no operation
1255
if (operation == null) {
1256            return IOperationHistory.NOTHING_TO_UNDO_STATUS;
1257        }
1258
1259        // error if operation is invalid
1260
if (!operation.canUndo()) {
1261            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
1262                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
1263
"Undo operation not valid - " + operation); //$NON-NLS-1$
1264
}
1265            return IOperationHistory.OPERATION_INVALID_STATUS;
1266        }
1267
1268        return doUndo(monitor, info, operation);
1269    }
1270
1271    /*
1272     * (non-Javadoc)
1273     *
1274     * @see org.eclipse.core.commands.operations.IOperationHistory#undoOperation(org.eclipse.core.commands.operations.IUndoableOperation,
1275     * org.eclipse.core.runtime.IProgressMonitor,
1276     * org.eclipse.core.runtime.IAdaptable)
1277     */

1278    public IStatus undoOperation(IUndoableOperation operation,
1279            IProgressMonitor monitor, IAdaptable info)
1280            throws ExecutionException {
1281        Assert.isNotNull(operation);
1282        IStatus status;
1283        if (operation.canUndo()) {
1284            status = doUndo(monitor, info, operation);
1285        } else {
1286            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
1287                Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
1288
"Undo operation not valid - " + operation); //$NON-NLS-1$
1289
}
1290            status = IOperationHistory.OPERATION_INVALID_STATUS;
1291        }
1292        return status;
1293    }
1294
1295    /*
1296     * (non-Javadoc)
1297     *
1298     * @see org.eclipse.core.commands.operations.IOperationHistory#openOperation(org.eclipse.core.commands.operations.ICompositeOperation)
1299     */

1300    public void openOperation(ICompositeOperation operation, int mode) {
1301        synchronized (openCompositeLock) {
1302            if (openComposite != null && openComposite != operation) {
1303                // unexpected nesting of operations.
1304
if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
1305                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
1306
"Open operation called while another operation is open. old: " //$NON-NLS-1$
1307
+ openComposite + "; new: " + operation); //$NON-NLS-1$
1308
}
1309
1310                throw new IllegalStateException JavaDoc(
1311                        "Cannot open an operation while one is already open"); //$NON-NLS-1$
1312
}
1313            openComposite = operation;
1314        }
1315        if (DEBUG_OPERATION_HISTORY_OPENOPERATION) {
1316            Tracing.printTrace("OPERATIONHISTORY", "Opening operation " //$NON-NLS-1$ //$NON-NLS-2$
1317
+ openComposite);
1318        }
1319
1320        if (mode == EXECUTE) {
1321            notifyAboutToExecute(openComposite);
1322        }
1323    }
1324
1325    /*
1326     * (non-Javadoc)
1327     *
1328     * @see org.eclipse.core.commands.operations.IOperationHistory#closeOperation(boolean,
1329     * boolean)
1330     */

1331    public void closeOperation(boolean operationOK, boolean addToHistory,
1332            int mode) {
1333        ICompositeOperation endedComposite = null;
1334
1335        synchronized (openCompositeLock) {
1336            if (DEBUG_OPERATION_HISTORY_UNEXPECTED) {
1337                if (openComposite == null) {
1338                    Tracing.printTrace("OPERATIONHISTORY", //$NON-NLS-1$
1339
"Attempted to close operation when none was open"); //$NON-NLS-1$
1340
return;
1341                }
1342            }
1343            // notifications will occur outside the synchonized block
1344
if (openComposite != null) {
1345                if (DEBUG_OPERATION_HISTORY_OPENOPERATION) {
1346                    Tracing.printTrace("OPERATIONHISTORY", "Closing operation " //$NON-NLS-1$ //$NON-NLS-2$
1347
+ openComposite);
1348                }
1349                endedComposite = openComposite;
1350                openComposite = null;
1351            }
1352        }
1353        // any mode other than EXECUTE was triggered by a request to undo or
1354
// redo something already in the history, so undo and redo
1355
// notification will occur at the end of that sequence.
1356
if (endedComposite != null) {
1357            if (operationOK) {
1358                if (mode == EXECUTE) {
1359                    notifyDone(endedComposite);
1360                }
1361                if (addToHistory) {
1362                    add(endedComposite);
1363                }
1364            } else {
1365                if (mode == EXECUTE) {
1366                    notifyNotOK(endedComposite);
1367                }
1368            }
1369        }
1370    }
1371
1372    /*
1373     * (non-Javadoc)
1374     *
1375     * @see org.eclipse.core.commands.operations.IOperationHistory#operationChanged(org.eclipse.core.commands.operations.IUndoableOperation)
1376     */

1377    public void operationChanged(IUndoableOperation operation) {
1378        if (undoList.contains(operation) || redoList.contains(operation)) {
1379            notifyChanged(operation);
1380        }
1381    }
1382}
1383
Popular Tags