KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 2005, 2006 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.List JavaDoc;
15
16 import org.eclipse.core.commands.ExecutionException;
17 import org.eclipse.core.runtime.IAdaptable;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.IStatus;
20 import org.eclipse.core.runtime.OperationCanceledException;
21 import org.eclipse.core.runtime.Status;
22
23 /**
24  * Triggered operations are a specialized implementation of a composite
25  * operation that keeps track of operations triggered by the execution of some
26  * primary operation. The composite knows which operation was the trigger for
27  * subsequent operations, and adds all triggered operations as children. When
28  * execution, undo, or redo is performed, only the triggered operation is
29  * executed, undone, or redone if it is still present. If the trigger is removed
30  * from the triggered operations, then the child operations will replace the
31  * triggered operations in the history.
32  * <p>
33  * This class may be instantiated by clients.
34  * </p>
35  *
36  * @since 3.1
37  */

38 public final class TriggeredOperations extends AbstractOperation implements
39         ICompositeOperation, IAdvancedUndoableOperation,
40         IContextReplacingOperation {
41
42     private IUndoableOperation triggeringOperation;
43
44     private IOperationHistory history;
45
46     private List JavaDoc children = new ArrayList JavaDoc();
47
48     /**
49      * Construct a composite triggered operations using the specified undoable
50      * operation as the trigger. Use the label of this trigger as the label of
51      * the operation.
52      *
53      * @param operation
54      * the operation that will trigger other operations.
55      * @param history
56      * the operation history containing the triggered operations.
57      */

58     public TriggeredOperations(IUndoableOperation operation,
59             IOperationHistory history) {
60         super(operation.getLabel());
61         triggeringOperation = operation;
62         recomputeContexts();
63         this.history = history;
64     }
65
66     /*
67      * (non-Javadoc)
68      *
69      * @see org.eclipse.core.commands.operations.IUndoableOperation#add(org.eclipse.core.commands.operations.IUndoableOperation)
70      */

71     public void add(IUndoableOperation operation) {
72         children.add(operation);
73         recomputeContexts();
74     }
75
76     /*
77      * (non-Javadoc)
78      *
79      * @see org.eclipse.core.commands.operations.IUndoableOperation#remove(org.eclipse.core.commands.operations.IUndoableOperation)
80      */

81     public void remove(IUndoableOperation operation) {
82         if (operation == triggeringOperation) {
83             // the triggering operation is being removed, so we must replace
84
// this composite with its individual triggers.
85
triggeringOperation = null;
86             // save the children before replacing the operation, since this
87
// operation will be disposed as part of replacing it. We don't want
88
// the children to be disposed since they are to replace this
89
// operation.
90
List JavaDoc childrenToRestore = new ArrayList JavaDoc(children);
91             children = new ArrayList JavaDoc(0);
92             recomputeContexts();
93             operation.dispose();
94             // now replace the triggering operation
95
history.replaceOperation(this,
96                     (IUndoableOperation[]) childrenToRestore
97                             .toArray(new IUndoableOperation[childrenToRestore
98                                     .size()]));
99         } else {
100             children.remove(operation);
101             operation.dispose();
102             recomputeContexts();
103         }
104     }
105
106     /**
107      * Remove the specified context from the receiver. This method is typically
108      * invoked when the history is being flushed for a certain context. In the
109      * case of triggered operations, if the only context for the triggering
110      * operation is being removed, then the triggering operation must be
111      * replaced in the operation history with the atomic operations that it
112      * triggered. If the context being removed is not the only context for the
113      * triggering operation, the triggering operation will remain, and the
114      * children will each be similarly checked.
115      *
116      * @param context
117      * the undo context being removed from the receiver.
118      */

119     public void removeContext(IUndoContext context) {
120
121         boolean recompute = false;
122         // first check to see if we are removing the only context of the
123
// triggering operation
124
if (triggeringOperation != null
125                 && triggeringOperation.hasContext(context)) {
126             if (triggeringOperation.getContexts().length == 1) {
127                 remove(triggeringOperation);
128                 return;
129             }
130             triggeringOperation.removeContext(context);
131             recompute = true;
132         }
133         // the triggering operation remains, check all the children
134
ArrayList JavaDoc toBeRemoved = new ArrayList JavaDoc();
135         for (int i = 0; i < children.size(); i++) {
136             IUndoableOperation child = (IUndoableOperation) children.get(i);
137             if (child.hasContext(context)) {
138                 if (child.getContexts().length == 1) {
139                     toBeRemoved.add(child);
140                 } else {
141                     child.removeContext(context);
142                 }
143                 recompute = true;
144             }
145         }
146         for (int i = 0; i < toBeRemoved.size(); i++) {
147             remove((IUndoableOperation) toBeRemoved.get(i));
148         }
149         if (recompute) {
150             recomputeContexts();
151         }
152     }
153
154     /*
155      * (non-Javadoc)
156      *
157      * @see org.eclipse.core.commands.operations.IUndoableOperation#execute(org.eclipse.core.runtime.IProgressMonitor,
158      * org.eclipse.core.runtime.IAdaptable)
159      */

160     public IStatus execute(IProgressMonitor monitor, IAdaptable info)
161             throws ExecutionException {
162         if (triggeringOperation != null) {
163             history.openOperation(this, IOperationHistory.EXECUTE);
164             try {
165                 IStatus status = triggeringOperation.execute(monitor, info);
166                 history.closeOperation(status.isOK(), false,
167                         IOperationHistory.EXECUTE);
168                 return status;
169             } catch (ExecutionException e) {
170                 history.closeOperation(false, false, IOperationHistory.EXECUTE);
171                 throw e;
172             } catch (RuntimeException JavaDoc e) {
173                 history.closeOperation(false, false, IOperationHistory.EXECUTE);
174                 throw e;
175             }
176
177         }
178         return IOperationHistory.OPERATION_INVALID_STATUS;
179     }
180
181     /*
182      * (non-Javadoc)
183      *
184      * @see org.eclipse.core.commands.operations.IUndoableOperation#redo(org.eclipse.core.runtime.IProgressMonitor,
185      * org.eclipse.core.runtime.IAdaptable)
186      */

187     public IStatus redo(IProgressMonitor monitor, IAdaptable info)
188             throws ExecutionException {
189         if (triggeringOperation != null) {
190             history.openOperation(this, IOperationHistory.REDO);
191             List JavaDoc childrenToRestore = new ArrayList JavaDoc(children);
192             try {
193                 removeAllChildren();
194                 IStatus status = triggeringOperation.redo(monitor, info);
195                 if (!status.isOK()) {
196                     children = childrenToRestore;
197                 }
198                 history.closeOperation(status.isOK(), false,
199                         IOperationHistory.REDO);
200                 return status;
201             } catch (ExecutionException e) {
202                 children = childrenToRestore;
203                 history.closeOperation(false, false, IOperationHistory.REDO);
204                 throw e;
205             } catch (RuntimeException JavaDoc e) {
206                 children = childrenToRestore;
207                 history.closeOperation(false, false, IOperationHistory.REDO);
208                 throw e;
209             }
210         }
211         return IOperationHistory.OPERATION_INVALID_STATUS;
212     }
213
214     /*
215      * (non-Javadoc)
216      *
217      * @see org.eclipse.core.commands.operations.IUndoableOperation#undo(org.eclipse.core.runtime.IProgressMonitor,
218      * org.eclipse.core.runtime.IAdaptable)
219      */

220     public IStatus undo(IProgressMonitor monitor, IAdaptable info)
221             throws ExecutionException {
222         if (triggeringOperation != null) {
223             history.openOperation(this, IOperationHistory.UNDO);
224             List JavaDoc childrenToRestore = new ArrayList JavaDoc(children);
225             try {
226                 removeAllChildren();
227                 IStatus status = triggeringOperation.undo(monitor, info);
228                 if (!status.isOK()) {
229                     children = childrenToRestore;
230                 }
231                 history.closeOperation(status.isOK(), false,
232                         IOperationHistory.UNDO);
233                 return status;
234             } catch (ExecutionException e) {
235                 children = childrenToRestore;
236                 history.closeOperation(false, false, IOperationHistory.UNDO);
237                 throw e;
238             } catch (RuntimeException JavaDoc e) {
239                 children = childrenToRestore;
240                 history.closeOperation(false, false, IOperationHistory.UNDO);
241                 throw e;
242             }
243         }
244         return IOperationHistory.OPERATION_INVALID_STATUS;
245     }
246
247     /*
248      * (non-Javadoc)
249      *
250      * @see org.eclipse.core.commands.operations.IUndoableOperation#canUndo()
251      */

252     public boolean canUndo() {
253         if (triggeringOperation != null) {
254             return triggeringOperation.canUndo();
255         }
256         return false;
257     }
258
259     /*
260      * (non-Javadoc)
261      *
262      * @see org.eclipse.core.commands.operations.IUndoableOperation#canExecute()
263      */

264     public boolean canExecute() {
265         if (triggeringOperation != null) {
266             return triggeringOperation.canExecute();
267         }
268         return false;
269     }
270
271     /*
272      * (non-Javadoc)
273      *
274      * @see org.eclipse.core.commands.operations.IUndoableOperation#canRedo()
275      */

276     public boolean canRedo() {
277         if (triggeringOperation != null) {
278             return triggeringOperation.canRedo();
279         }
280         return false;
281     }
282
283     /*
284      * Dispose all operations in the receiver.
285      */

286     public void dispose() {
287         for (int i = 0; i < children.size(); i++) {
288             ((IUndoableOperation) (children.get(i))).dispose();
289         }
290         if (triggeringOperation != null) {
291             triggeringOperation.dispose();
292         }
293     }
294
295     /*
296      * Recompute contexts in light of some change in the children
297      */

298     private void recomputeContexts() {
299         ArrayList JavaDoc allContexts = new ArrayList JavaDoc();
300         if (triggeringOperation != null) {
301             IUndoContext[] contexts = triggeringOperation.getContexts();
302             for (int i = 0; i < contexts.length; i++) {
303                 allContexts.add(contexts[i]);
304             }
305         }
306         for (int i = 0; i < children.size(); i++) {
307             IUndoContext[] contexts = ((IUndoableOperation) children.get(i))
308                     .getContexts();
309             for (int j = 0; j < contexts.length; j++) {
310                 if (!allContexts.contains(contexts[j])) {
311                     allContexts.add(contexts[j]);
312                 }
313             }
314         }
315         contexts = allContexts;
316
317     }
318
319     /*
320      * Remove all non-triggering children
321      */

322     private void removeAllChildren() {
323         IUndoableOperation[] nonTriggers = (IUndoableOperation[]) children
324                 .toArray(new IUndoableOperation[children.size()]);
325         for (int i = 0; i < nonTriggers.length; i++) {
326             children.remove(nonTriggers[i]);
327             nonTriggers[i].dispose();
328         }
329     }
330
331     /**
332      * Return the operation that triggered the other operations in this
333      * composite.
334      *
335      * @return the IUndoableOperation that triggered the other children.
336      */

337     public IUndoableOperation getTriggeringOperation() {
338         return triggeringOperation;
339     }
340
341     /*
342      * (non-Javadoc)
343      *
344      * @see org.eclipse.core.commands.operations.IAdvancedModelOperation#getAffectedObjects()
345      */

346     public Object JavaDoc[] getAffectedObjects() {
347         if (triggeringOperation instanceof IAdvancedUndoableOperation) {
348             return ((IAdvancedUndoableOperation) triggeringOperation)
349                     .getAffectedObjects();
350         }
351         return null;
352     }
353
354     /*
355      * (non-Javadoc)
356      *
357      * @see org.eclipse.core.commands.operations.IAdvancedModelOperation#aboutToNotify(org.eclipse.core.commands.operations.OperationHistoryEvent)
358      */

359     public void aboutToNotify(OperationHistoryEvent event) {
360         if (triggeringOperation instanceof IAdvancedUndoableOperation) {
361             ((IAdvancedUndoableOperation) triggeringOperation)
362                     .aboutToNotify(event);
363         }
364     }
365
366     /*
367      * (non-Javadoc)
368      *
369      * @see org.eclipse.core.commands.operations.IAdvancedUndoableOperation#computeUndoableStatus(org.eclipse.core.runtime.IProgressMonitor)
370      */

371     public IStatus computeUndoableStatus(IProgressMonitor monitor)
372             throws ExecutionException {
373         if (triggeringOperation instanceof IAdvancedUndoableOperation) {
374             try {
375                 return ((IAdvancedUndoableOperation) triggeringOperation)
376                         .computeUndoableStatus(monitor);
377             } catch (OperationCanceledException e) {
378                 return Status.CANCEL_STATUS;
379             }
380         }
381         return Status.OK_STATUS;
382
383     }
384
385     /*
386      * (non-Javadoc)
387      *
388      * @see org.eclipse.core.commands.operations.IAdvancedUndoableOperation#computeRedoableStatus(org.eclipse.core.runtime.IProgressMonitor)
389      */

390     public IStatus computeRedoableStatus(IProgressMonitor monitor)
391             throws ExecutionException {
392         if (triggeringOperation instanceof IAdvancedUndoableOperation) {
393             try {
394                 return ((IAdvancedUndoableOperation) triggeringOperation)
395                         .computeRedoableStatus(monitor);
396             } catch (OperationCanceledException e) {
397                 return Status.CANCEL_STATUS;
398             }
399         }
400         return Status.OK_STATUS;
401
402     }
403
404     /**
405      * Replace the undo context of the receiver with the provided replacement
406      * undo context. In the case of triggered operations, all contained
407      * operations are checked and any occurrence of the original context is
408      * replaced with the new undo context.
409      * <p>
410      * This message has no effect if the original undo context is not present in
411      * the receiver.
412      *
413      * @param original
414      * the undo context which is to be replaced
415      * @param replacement
416      * the undo context which is replacing the original
417      * @since 3.2
418      */

419     public void replaceContext(IUndoContext original, IUndoContext replacement) {
420
421         // first check the triggering operation
422
if (triggeringOperation != null
423                 && triggeringOperation.hasContext(original)) {
424             if (triggeringOperation instanceof IContextReplacingOperation) {
425                 ((IContextReplacingOperation) triggeringOperation)
426                         .replaceContext(original, replacement);
427             } else {
428                 triggeringOperation.removeContext(original);
429                 triggeringOperation.addContext(replacement);
430             }
431         }
432         // Now check all the children
433
for (int i = 0; i < children.size(); i++) {
434             IUndoableOperation child = (IUndoableOperation) children.get(i);
435             if (child.hasContext(original)) {
436                 if (child instanceof IContextReplacingOperation) {
437                     ((IContextReplacingOperation) child).replaceContext(
438                             original, replacement);
439                 } else {
440                     child.removeContext(original);
441                     child.addContext(replacement);
442                 }
443             }
444         }
445         recomputeContexts();
446     }
447
448     /**
449      * Add the specified context to the operation. Overridden in
450      * TriggeredOperations to add the specified undo context to the triggering
451      * operation.
452      *
453      * @param context
454      * the context to be added
455      *
456      * @since 3.2
457      */

458     public void addContext(IUndoContext context) {
459         if (triggeringOperation != null) {
460             triggeringOperation.addContext(context);
461             recomputeContexts();
462         }
463     }
464
465 }
466
Popular Tags