Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 5973347c169863fd545963673325eeaca19abb9b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*****************************************************************************
 * Copyright (c) 2011, 2014 Atos, CEA, and others.
 *
 *    
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Mathieu Velten (Atos) - Initial API and implementation
 *  Christian W. Damus (CEA) - bug 357250
 *
 *****************************************************************************/
package org.eclipse.papyrus.commands;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.ICompositeOperation;
import org.eclipse.core.commands.operations.IOperationApprover;
import org.eclipse.core.commands.operations.IOperationApprover2;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IOperationHistoryListener;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.workspace.EMFCommandOperation;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.papyrus.commands.wrappers.GMFtoEMFCommandWrapper;

public class CheckedOperationHistory implements IOperationHistory {

	private static class CheckedOperationHistoryHolder {

		public static final CheckedOperationHistory instance = new CheckedOperationHistory();
	}

	public static CheckedOperationHistory getInstance() {
		return CheckedOperationHistoryHolder.instance;
	}

	protected static final IOperationApprover2[] approversArray;

	protected IOperationHistory history;

	private static class ApproverPriorityPair implements Comparable<ApproverPriorityPair> {

		public IOperationApprover2 approver;

		public int priority;

		public int compareTo(ApproverPriorityPair o) {
			if(o.priority > priority) {
				return 1;
			} else if(o.priority < priority) {
				return -1;
			} else {
				return 0;
			}
		}

	}

	static {
		IConfigurationElement[] configElements = Platform.getExtensionRegistry().getConfigurationElementsFor(Activator.PLUGIN_ID, "operationApprover"); //$NON-NLS-1$

		List<ApproverPriorityPair> approverPriorityPairs = new LinkedList<ApproverPriorityPair>();
		for(IConfigurationElement elem : configElements) {
			if("operationApprover".equals(elem.getName())) { //$NON-NLS-1$
				try {
					ApproverPriorityPair approverPriorityPair = new ApproverPriorityPair();
					approverPriorityPair.approver = (IOperationApprover2)elem.createExecutableExtension("class");
					approverPriorityPair.priority = Integer.parseInt(elem.getAttribute("priority"));

					approverPriorityPairs.add(approverPriorityPair);
				} catch (Exception e) {
					Activator.log.error("Uncaught exception in instantiation of operation approver.", e); //$NON-NLS-1$
				}
			}
		}

		Collections.sort(approverPriorityPairs);

		approversArray = new IOperationApprover2[approverPriorityPairs.size()];

		for(int i = 0; i < approversArray.length; i++) {
			approversArray[i] = approverPriorityPairs.get(i).approver;
		}
	}

	private CheckedOperationHistory() {
		history = OperationHistoryFactory.getOperationHistory();
	}

	/*
	 * Consult the IOperationApprovers to see if the proposed redo should be
	 * allowed.
	 */
	protected IStatus getRedoApproval(IUndoableOperation operation, IAdaptable info) {
		operation = unwrap(operation);
		for(int i = 0; i < approversArray.length; i++) {
			IStatus approval = approversArray[i].proceedRedoing(operation, this, info);
			if(!approval.isOK()) {
				return approval;
			}
		}
		return Status.OK_STATUS;
	}

	/*
	 * Consult the IOperationApprovers to see if the proposed undo should be
	 * allowed.
	 */
	protected IStatus getUndoApproval(IUndoableOperation operation, IAdaptable info) {
		operation = unwrap(operation);
		for(int i = 0; i < approversArray.length; i++) {
			IStatus approval = approversArray[i].proceedUndoing(operation, this, info);
			if(!approval.isOK()) {
				return approval;
			}
		}
		return Status.OK_STATUS;
	}

	/*
	 * Consult the IOperationApprovers to see if the proposed execution should
	 * be allowed.
	 * 
	 * @since 3.2
	 */
	protected IStatus getExecuteApproval(IUndoableOperation operation, IAdaptable info) {
		operation = unwrap(operation);
		for(int i = 0; i < approversArray.length; i++) {
			IStatus approval = approversArray[i].proceedExecuting(operation, this, info);
			if(!approval.isOK()) {
				return approval;
			}
		}
		return Status.OK_STATUS;
	}

	/**
	 * the unified command stack wraps ICommand GMFtoEMFCommandWrapper
	 * which are wrapped in EMFCommandOperation,
	 * unwrap it before validation
	 * 
	 * @param operation
	 * @return
	 */
	protected IUndoableOperation unwrap(IUndoableOperation operation) {
		if(operation instanceof EMFCommandOperation) {
			Command emfCommand = ((EMFCommandOperation)operation).getCommand();
			if(emfCommand instanceof GMFtoEMFCommandWrapper) {
				ICommand gmfCommand = ((GMFtoEMFCommandWrapper)emfCommand).getGMFCommand();
				if(gmfCommand != null) {
					return gmfCommand;
				}
			}
		}

		return operation;
	}

	public IStatus execute(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
		// check with the operation approvers
		IStatus status = getExecuteApproval(operation, info);
		if(!status.isOK()) {
			// not approved. No notifications are sent, just return the status.
			return status;
		}
		return history.execute(operation, monitor, info);
	}

	public IStatus undo(IUndoContext context, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
		Assert.isNotNull(context);
		IUndoableOperation operation = getUndoOperation(context);

		// info if there is no operation
		if(operation == null) {
			return IOperationHistory.NOTHING_TO_UNDO_STATUS;
		}

		// check with the operation approvers
		IStatus status = getUndoApproval(operation, info);
		if(!status.isOK()) {
			// not approved. No notifications are sent, just return the status.
			return status;
		}
		return history.undo(context, monitor, info);
	}

	public IStatus redo(IUndoContext context, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
		Assert.isNotNull(context);
		IUndoableOperation operation = getRedoOperation(context);

		// info if there is no operation
		if(operation == null) {
			return IOperationHistory.NOTHING_TO_REDO_STATUS;
		}

		// check with the operation approvers
		IStatus status = getRedoApproval(operation, info);
		if(!status.isOK()) {
			// not approved. No notifications are sent, just return the status.
			return status;
		}
		return history.redo(context, monitor, info);
	}

	// all the following methods are pure delegation

	public IStatus undoOperation(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
		return history.undoOperation(operation, monitor, info);
	}

	public void setLimit(IUndoContext context, int limit) {
		history.setLimit(context, limit);
	}

	public void replaceOperation(IUndoableOperation operation, IUndoableOperation[] replacements) {
		history.replaceOperation(operation, replacements);
	}

	public void removeOperationHistoryListener(IOperationHistoryListener listener) {
		history.removeOperationHistoryListener(listener);
	}

	public void removeOperationApprover(IOperationApprover approver) {
		history.removeOperationApprover(approver);
	}

	public IStatus redoOperation(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
		return history.redoOperation(operation, monitor, info);
	}

	public void operationChanged(IUndoableOperation operation) {
		history.operationChanged(operation);
	}

	public void openOperation(ICompositeOperation operation, int mode) {
		history.openOperation(operation, mode);
	}

	public IUndoableOperation getUndoOperation(IUndoContext context) {
		return history.getUndoOperation(context);
	}

	public IUndoableOperation[] getUndoHistory(IUndoContext context) {
		return history.getUndoHistory(context);
	}

	public IUndoableOperation getRedoOperation(IUndoContext context) {
		return history.getRedoOperation(context);
	}

	public IUndoableOperation[] getRedoHistory(IUndoContext context) {
		return history.getRedoHistory(context);
	}

	public int getLimit(IUndoContext context) {
		return history.getLimit(context);
	}

	public void dispose(IUndoContext context, boolean flushUndo, boolean flushRedo, boolean flushContext) {
		history.dispose(context, flushUndo, flushRedo, flushContext);
	}

	public void closeOperation(boolean operationOK, boolean addToHistory, int mode) {
		history.closeOperation(operationOK, addToHistory, mode);
	}

	public boolean canUndo(IUndoContext context) {
		return history.canUndo(context);
	}

	public boolean canRedo(IUndoContext context) {
		return history.canRedo(context);
	}

	public void addOperationHistoryListener(IOperationHistoryListener listener) {
		history.addOperationHistoryListener(listener);
	}

	public void addOperationApprover(IOperationApprover approver) {
		history.addOperationApprover(approver);
	}

	public void add(IUndoableOperation operation) {
		history.add(operation);
	}
}

Back to the top