comparison dwtx/core/commands/operations/TriggeredOperations.d @ 3:6518c18a01f7

eclipse.core package without osgi dependencies
author Frank Benoit <benoit@tionex.de>
date Wed, 26 Mar 2008 00:57:19 +0100
parents
children ea8ff534f622
comparison
equal deleted inserted replaced
2:a012107a911c 3:6518c18a01f7
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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module dwtx.core.commands.operations.TriggeredOperations;
14
15 import tango.util.collection.ArraySeq;
16 import tango.util.collection.model.Seq;
17
18 import dwtx.core.commands.ExecutionException;
19 import dwtx.core.runtime.IAdaptable;
20 import dwtx.core.runtime.IProgressMonitor;
21 import dwtx.core.runtime.IStatus;
22 import dwtx.core.runtime.OperationCanceledException;
23 import dwtx.core.runtime.Status;
24
25 import dwtx.core.commands.operations.AbstractOperation;
26 import dwtx.core.commands.operations.ICompositeOperation;
27 import dwtx.core.commands.operations.IAdvancedUndoableOperation;
28 import dwtx.core.commands.operations.IContextReplacingOperation;
29 import dwtx.core.commands.operations.IUndoableOperation;
30 import dwtx.core.commands.operations.IOperationHistory;
31 import dwtx.core.commands.operations.IUndoContext;
32 import dwtx.core.commands.operations.OperationHistoryEvent;
33
34 import dwt.dwthelper.utils;
35
36 /**
37 * Triggered operations are a specialized implementation of a composite
38 * operation that keeps track of operations triggered by the execution of some
39 * primary operation. The composite knows which operation was the trigger for
40 * subsequent operations, and adds all triggered operations as children. When
41 * execution, undo, or redo is performed, only the triggered operation is
42 * executed, undone, or redone if it is still present. If the trigger is removed
43 * from the triggered operations, then the child operations will replace the
44 * triggered operations in the history.
45 * <p>
46 * This class may be instantiated by clients.
47 * </p>
48 *
49 * @since 3.1
50 */
51 public final class TriggeredOperations : AbstractOperation,
52 ICompositeOperation, IAdvancedUndoableOperation,
53 IContextReplacingOperation {
54
55 private IUndoableOperation triggeringOperation;
56
57 private IOperationHistory history;
58
59 private Seq!(IUndoableOperation) children;
60
61 /**
62 * Construct a composite triggered operations using the specified undoable
63 * operation as the trigger. Use the label of this trigger as the label of
64 * the operation.
65 *
66 * @param operation
67 * the operation that will trigger other operations.
68 * @param history
69 * the operation history containing the triggered operations.
70 */
71 public this(IUndoableOperation operation,
72 IOperationHistory history) {
73 super(operation.getLabel());
74 children = new ArraySeq!(IUndoableOperation);
75 triggeringOperation = operation;
76 recomputeContexts();
77 this.history = history;
78 }
79
80 /*
81 * (non-Javadoc)
82 *
83 * @see dwtx.core.commands.operations.IUndoableOperation#add(dwtx.core.commands.operations.IUndoableOperation)
84 */
85 public void add(IUndoableOperation operation) {
86 children.append(operation);
87 recomputeContexts();
88 }
89
90 /*
91 * (non-Javadoc)
92 *
93 * @see dwtx.core.commands.operations.IUndoableOperation#remove(dwtx.core.commands.operations.IUndoableOperation)
94 */
95 public void remove(IUndoableOperation operation) {
96 if (operation is triggeringOperation) {
97 // the triggering operation is being removed, so we must replace
98 // this composite with its individual triggers.
99 triggeringOperation = null;
100 // save the children before replacing the operation, since this
101 // operation will be disposed as part of replacing it. We don't want
102 // the children to be disposed since they are to replace this
103 // operation.
104 Seq!(IUndoableOperation) childrenToRestore = children.dup;
105 children = new ArraySeq!(IUndoableOperation);
106 recomputeContexts();
107 operation.dispose();
108 // now replace the triggering operation
109 history.replaceOperation(this, childrenToRestore.toArray());
110 } else {
111 children.remove(operation);
112 operation.dispose();
113 recomputeContexts();
114 }
115 }
116
117 /**
118 * Remove the specified context from the receiver. This method is typically
119 * invoked when the history is being flushed for a certain context. In the
120 * case of triggered operations, if the only context for the triggering
121 * operation is being removed, then the triggering operation must be
122 * replaced in the operation history with the atomic operations that it
123 * triggered. If the context being removed is not the only context for the
124 * triggering operation, the triggering operation will remain, and the
125 * children will each be similarly checked.
126 *
127 * @param context
128 * the undo context being removed from the receiver.
129 */
130 public void removeContext(IUndoContext context) {
131
132 bool recompute = false;
133 // first check to see if we are removing the only context of the
134 // triggering operation
135 if (triggeringOperation !is null
136 && triggeringOperation.hasContext(context)) {
137 if (triggeringOperation.getContexts().length is 1) {
138 remove(triggeringOperation);
139 return;
140 }
141 triggeringOperation.removeContext(context);
142 recompute = true;
143 }
144 // the triggering operation remains, check all the children
145 auto toBeRemoved = new ArraySeq!(IUndoableOperation);
146 for (int i = 0; i < children.size(); i++) {
147 IUndoableOperation child = cast(IUndoableOperation) children.get(i);
148 if (child.hasContext(context)) {
149 if (child.getContexts().length is 1) {
150 toBeRemoved.append(child);
151 } else {
152 child.removeContext(context);
153 }
154 recompute = true;
155 }
156 }
157 for (int i = 0; i < toBeRemoved.size(); i++) {
158 remove(cast(IUndoableOperation) toBeRemoved.get(i));
159 }
160 if (recompute) {
161 recomputeContexts();
162 }
163 }
164
165 /*
166 * (non-Javadoc)
167 *
168 * @see dwtx.core.commands.operations.IUndoableOperation#execute(dwtx.core.runtime.IProgressMonitor,
169 * dwtx.core.runtime.IAdaptable)
170 */
171 public IStatus execute(IProgressMonitor monitor, IAdaptable info) {
172 if (triggeringOperation !is null) {
173 history.openOperation(this, IOperationHistory.EXECUTE);
174 try {
175 IStatus status = triggeringOperation.execute(monitor, info);
176 history.closeOperation(status.isOK(), false,
177 IOperationHistory.EXECUTE);
178 return status;
179 } catch (ExecutionException e) {
180 history.closeOperation(false, false, IOperationHistory.EXECUTE);
181 throw e;
182 } catch (RuntimeException e) {
183 history.closeOperation(false, false, IOperationHistory.EXECUTE);
184 throw e;
185 }
186
187 }
188 return IOperationHistory.OPERATION_INVALID_STATUS;
189 }
190
191 /*
192 * (non-Javadoc)
193 *
194 * @see dwtx.core.commands.operations.IUndoableOperation#redo(dwtx.core.runtime.IProgressMonitor,
195 * dwtx.core.runtime.IAdaptable)
196 */
197 public IStatus redo(IProgressMonitor monitor, IAdaptable info) {
198 if (triggeringOperation !is null) {
199 history.openOperation(this, IOperationHistory.REDO);
200 Seq!(IUndoableOperation) childrenToRestore = children.dup;
201 try {
202 removeAllChildren();
203 IStatus status = triggeringOperation.redo(monitor, info);
204 if (!status.isOK()) {
205 children = childrenToRestore;
206 }
207 history.closeOperation(status.isOK(), false,
208 IOperationHistory.REDO);
209 return status;
210 } catch (ExecutionException e) {
211 children = childrenToRestore;
212 history.closeOperation(false, false, IOperationHistory.REDO);
213 throw e;
214 } catch (RuntimeException e) {
215 children = childrenToRestore;
216 history.closeOperation(false, false, IOperationHistory.REDO);
217 throw e;
218 }
219 }
220 return IOperationHistory.OPERATION_INVALID_STATUS;
221 }
222
223 /*
224 * (non-Javadoc)
225 *
226 * @see dwtx.core.commands.operations.IUndoableOperation#undo(dwtx.core.runtime.IProgressMonitor,
227 * dwtx.core.runtime.IAdaptable)
228 */
229 public IStatus undo(IProgressMonitor monitor, IAdaptable info) {
230 if (triggeringOperation !is null) {
231 history.openOperation(this, IOperationHistory.UNDO);
232 auto childrenToRestore = children.dup;
233 try {
234 removeAllChildren();
235 IStatus status = triggeringOperation.undo(monitor, info);
236 if (!status.isOK()) {
237 children = childrenToRestore;
238 }
239 history.closeOperation(status.isOK(), false,
240 IOperationHistory.UNDO);
241 return status;
242 } catch (ExecutionException e) {
243 children = childrenToRestore;
244 history.closeOperation(false, false, IOperationHistory.UNDO);
245 throw e;
246 } catch (RuntimeException e) {
247 children = childrenToRestore;
248 history.closeOperation(false, false, IOperationHistory.UNDO);
249 throw e;
250 }
251 }
252 return IOperationHistory.OPERATION_INVALID_STATUS;
253 }
254
255 /*
256 * (non-Javadoc)
257 *
258 * @see dwtx.core.commands.operations.IUndoableOperation#canUndo()
259 */
260 public bool canUndo() {
261 if (triggeringOperation !is null) {
262 return triggeringOperation.canUndo();
263 }
264 return false;
265 }
266
267 /*
268 * (non-Javadoc)
269 *
270 * @see dwtx.core.commands.operations.IUndoableOperation#canExecute()
271 */
272 public bool canExecute() {
273 if (triggeringOperation !is null) {
274 return triggeringOperation.canExecute();
275 }
276 return false;
277 }
278
279 /*
280 * (non-Javadoc)
281 *
282 * @see dwtx.core.commands.operations.IUndoableOperation#canRedo()
283 */
284 public bool canRedo() {
285 if (triggeringOperation !is null) {
286 return triggeringOperation.canRedo();
287 }
288 return false;
289 }
290
291 /*
292 * Dispose all operations in the receiver.
293 */
294 public void dispose() {
295 for (int i = 0; i < children.size(); i++) {
296 children.get(i).dispose();
297 }
298 if (triggeringOperation !is null) {
299 triggeringOperation.dispose();
300 }
301 }
302
303 /*
304 * Recompute contexts in light of some change in the children
305 */
306 private void recomputeContexts() {
307 auto allContexts = new ArraySeq!(IUndoContext);
308 if (triggeringOperation !is null) {
309 IUndoContext[] contexts = triggeringOperation.getContexts();
310 for (int i = 0; i < contexts.length; i++) {
311 allContexts.append(contexts[i]);
312 }
313 }
314 for (int i = 0; i < children.size(); i++) {
315 IUndoContext[] contexts = children.get(i).getContexts();
316 for (int j = 0; j < contexts.length; j++) {
317 if (!allContexts.contains(contexts[j])) {
318 allContexts.append(contexts[j]);
319 }
320 }
321 }
322 contexts = allContexts;
323
324 }
325
326 /*
327 * Remove all non-triggering children
328 */
329 private void removeAllChildren() {
330 IUndoableOperation[] nonTriggers = children.toArray();
331 for (int i = 0; i < nonTriggers.length; i++) {
332 children.remove(nonTriggers[i]);
333 nonTriggers[i].dispose();
334 }
335 }
336
337 /**
338 * Return the operation that triggered the other operations in this
339 * composite.
340 *
341 * @return the IUndoableOperation that triggered the other children.
342 */
343 public IUndoableOperation getTriggeringOperation() {
344 return triggeringOperation;
345 }
346
347 /*
348 * (non-Javadoc)
349 *
350 * @see dwtx.core.commands.operations.IAdvancedModelOperation#getAffectedObjects()
351 */
352 public Object[] getAffectedObjects() {
353 if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
354 return trg
355 .getAffectedObjects();
356 }
357 return null;
358 }
359
360 /*
361 * (non-Javadoc)
362 *
363 * @see dwtx.core.commands.operations.IAdvancedModelOperation#aboutToNotify(dwtx.core.commands.operations.OperationHistoryEvent)
364 */
365 public void aboutToNotify(OperationHistoryEvent event) {
366 if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
367 trg.aboutToNotify(event);
368 }
369 }
370
371 /*
372 * (non-Javadoc)
373 *
374 * @see dwtx.core.commands.operations.IAdvancedUndoableOperation#computeUndoableStatus(dwtx.core.runtime.IProgressMonitor)
375 */
376 public IStatus computeUndoableStatus(IProgressMonitor monitor) {
377 if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
378 try {
379 return trg.computeUndoableStatus(monitor);
380 } catch (OperationCanceledException e) {
381 return Status.CANCEL_STATUS;
382 }
383 }
384 return Status.OK_STATUS;
385
386 }
387
388 /*
389 * (non-Javadoc)
390 *
391 * @see dwtx.core.commands.operations.IAdvancedUndoableOperation#computeRedoableStatus(dwtx.core.runtime.IProgressMonitor)
392 */
393 public IStatus computeRedoableStatus(IProgressMonitor monitor) {
394 if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) {
395 try {
396 return trg.computeRedoableStatus(monitor);
397 } catch (OperationCanceledException e) {
398 return Status.CANCEL_STATUS;
399 }
400 }
401 return Status.OK_STATUS;
402
403 }
404
405 /**
406 * Replace the undo context of the receiver with the provided replacement
407 * undo context. In the case of triggered operations, all contained
408 * operations are checked and any occurrence of the original context is
409 * replaced with the new undo context.
410 * <p>
411 * This message has no effect if the original undo context is not present in
412 * the receiver.
413 *
414 * @param original
415 * the undo context which is to be replaced
416 * @param replacement
417 * the undo context which is replacing the original
418 * @since 3.2
419 */
420 public void replaceContext(IUndoContext original, IUndoContext replacement) {
421
422 // first check the triggering operation
423 if (triggeringOperation !is null
424 && triggeringOperation.hasContext(original)) {
425 if ( auto trg = cast(IContextReplacingOperation)triggeringOperation ) {
426 trg.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 = children.get(i);
435 if (child.hasContext(original)) {
436 if ( auto c = cast(IContextReplacingOperation)child ) {
437 c.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 !is null) {
460 triggeringOperation.addContext(context);
461 recomputeContexts();
462 }
463 }
464
465 }