Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7390874bed2a1bbd7c86e663977f156662f9bef7 (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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
/*******************************************************************************
 * Copyright (c) 2012, 2014 Wind River Systems, Inc. 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:
 * Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.tcf.te.tcf.ui.handler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.te.runtime.callback.Callback;
import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback;
import org.eclipse.tcf.te.runtime.persistence.interfaces.IURIPersistenceService;
import org.eclipse.tcf.te.runtime.services.ServiceManager;
import org.eclipse.tcf.te.runtime.services.ServiceUtils;
import org.eclipse.tcf.te.runtime.statushandler.StatusHandlerUtil;
import org.eclipse.tcf.te.runtime.utils.StatusHelper;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
import org.eclipse.tcf.te.tcf.locator.interfaces.services.IPeerModelRefreshService;
import org.eclipse.tcf.te.tcf.locator.model.ModelManager;
import org.eclipse.tcf.te.tcf.ui.help.IContextHelpIds;
import org.eclipse.tcf.te.tcf.ui.nls.Messages;
import org.eclipse.tcf.te.ui.interfaces.handler.IDeleteHandlerDelegate;
import org.eclipse.tcf.te.ui.views.Managers;
import org.eclipse.tcf.te.ui.views.ViewsUtil;
import org.eclipse.tcf.te.ui.views.interfaces.ICategory;
import org.eclipse.tcf.te.ui.views.interfaces.IUIConstants;
import org.eclipse.tcf.te.ui.views.interfaces.categories.ICategorizable;
import org.eclipse.tcf.te.ui.views.interfaces.categories.ICategoryManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;

/**
 * Delete handler implementation.
 */
public class DeleteHandler extends AbstractHandler {
	// Remember the shell from the execution event
	private Shell shell = null;

	/* (non-Javadoc)
	 * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
	 */
	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {
		// Get the shell
		shell = HandlerUtil.getActiveShell(event);
		// Get the current selection
		ISelection selection = getSelection(event);
		// Delete the selection
		if (selection != null) {
			delete(selection, new Callback() {
				@Override
				protected void internalDone(Object caller, IStatus status) {
					// Refresh the view
					ViewsUtil.refresh(IUIConstants.ID_EXPLORER);
				}
			});
		}
		// Reset the shell
		shell = null;

		return null;
	}

	protected ISelection getSelection(ExecutionEvent event) {
		return HandlerUtil.getCurrentSelection(event);
	}

	/**
	 * Tests if this delete handler can delete the elements of the given
	 * selection.
	 *
	 * @param selection The selection. Must not be <code>null</code>.
	 * @return <code>True</code> if the selection can be deleted by this handler, <code>false</code> otherwise.
	 */
	public boolean canDelete(ISelection selection) {
		Assert.isNotNull(selection);

		boolean canDelete = false;

		if (!(selection instanceof ITreeSelection) && selection instanceof IStructuredSelection && !selection.isEmpty()) {
			Iterator<?> it = ((IStructuredSelection)selection).iterator();
			List<TreePath> treePathes = new ArrayList<TreePath>();
			while (it.hasNext()) {
				Object sel = it.next();
				treePathes.add(new TreePath(new Object[]{sel}));
			}
			selection = new TreeSelection(treePathes.toArray(new TreePath[treePathes.size()]));
		}

		// The selection must be a tree selection and must not be empty
		if (selection instanceof ITreeSelection && !selection.isEmpty()) {
			// Assume the selection to be deletable
			canDelete = true;
			// Iterate the selection. All elements must be of type IPeerNode
			for (TreePath treePath : ((ITreeSelection)selection).getPaths()) {
				// Get the element
				Object element = treePath.getLastSegment();
				// This handler will take care of peer model nodes only
				if (!(element instanceof IPeerNode)) {
					canDelete = false;
					break;
				}

				// Check if there is a delete handler delegate for the element
				IDeleteHandlerDelegate delegate = ServiceUtils.getUIServiceDelegate(element, element, IDeleteHandlerDelegate.class);
				// If a delegate is available, ask the handler first if the given element is currently deletable
				if (delegate != null) canDelete = delegate.canDelete(treePath);

				if (!canDelete) {
					break;
				}
			}
		}

		return canDelete;
	}

	/**
	 * Internal helper class to describe the delete operation to perform.
	 */
	private static class Operation {
		// The operation types
		public enum TYPE { Remove, Unlink }

		// The element to operate on
		public IPeerNode node;
		// The operation type to perform
		public TYPE type;
		// In case of an "unlink" operation, the parent category
		// is required.
		public ICategory parentCategory;

		/**
		 * Constructor.
		 */
		public Operation() {
		}

		/**
		 * Executes the operation.
		 *
		 * @throws Exception if the operation fails.
		 */
		public void execute() throws Exception {
			Assert.isNotNull(node);
			Assert.isNotNull(type);

			if (TYPE.Remove.equals(type)) {
				// Remove the node from the persistence storage
				IURIPersistenceService service = ServiceManager.getInstance().getService(IURIPersistenceService.class);
				if (service == null) throw new IOException("Persistence service instance unavailable."); //$NON-NLS-1$
				service.delete(node, null);
				if (parentCategory != null) {
					ViewsUtil.setSelection(IUIConstants.ID_EXPLORER, new StructuredSelection(parentCategory));
				}

				// Check if there is a delete handler delegate for the element
				IDeleteHandlerDelegate delegate = ServiceUtils.getUIServiceDelegate(node, node, IDeleteHandlerDelegate.class);
				// If a delegate is available, signal the execution of the remove
				if (delegate != null) delegate.postDelete(node);
			}
			else if (TYPE.Unlink.equals(type)) {
				Assert.isNotNull(parentCategory);

				ICategoryManager manager = Managers.getCategoryManager();
				Assert.isNotNull(manager);

				ICategorizable categorizable = (ICategorizable)node.getAdapter(ICategorizable.class);
				if (categorizable == null) {
					categorizable = (ICategorizable)Platform.getAdapterManager().getAdapter(node, ICategorizable.class);
				}
				Assert.isNotNull(categorizable);

				manager.remove(parentCategory.getId(), categorizable.getId());
				ViewsUtil.setSelection(IUIConstants.ID_EXPLORER, new StructuredSelection(parentCategory));
			}
		}
	}

	/**
	 * Deletes all elements from the given selection and invokes the
	 * given callback once done.
	 *
	 * @param selection The selection. Must not be <code>null</code>.
	 * @param callback The callback. Must not be <code>null</code>.
	 */
	public void delete(ISelection selection, final ICallback callback) {
		Assert.isNotNull(selection);
		Assert.isNotNull(callback);

		// The callback needs to be invoked in any case. However, if called
		// from an asynchronous callback, set this flag to false.
		boolean invokeCallback = true;

		if (!(selection instanceof ITreeSelection) && selection instanceof IStructuredSelection && !selection.isEmpty()) {
			Iterator<?> it = ((IStructuredSelection)selection).iterator();
			List<TreePath> treePathes = new ArrayList<TreePath>();
			while (it.hasNext()) {
				Object sel = it.next();
				treePathes.add(new TreePath(new Object[]{sel}));
			}
			selection = new TreeSelection(treePathes.toArray(new TreePath[treePathes.size()]));
		}

		// The selection must be a tree selection and must not be empty
		if (selection instanceof ITreeSelection && !selection.isEmpty()) {
			// Determine the operations to perform for each of the selected elements
			Operation[] operations = selection2operations((ITreeSelection)selection);

			// Seek confirmation for the "remove" operations. If the user deny it,
			// everything, including the "unlink" operations are cancelled.
			boolean confirmed = confirmDelete(operations);

			// Execute the operations
			if (confirmed) {
				// If one of the operation is a "remove" operation, the locator
				// model needs to be refreshed
				boolean refreshModel = false;

				try {
					for (Operation op : operations) {
						if (Operation.TYPE.Remove.equals(op.type)) {
							refreshModel = true;
						}
						op.execute();
					}
				} catch (Exception e) {
					String template = NLS.bind(Messages.DeleteHandler_error_deleteFailed, Messages.PossibleCause);
					StatusHandlerUtil.handleStatus(StatusHelper.getStatus(e), selection, template,
													Messages.DeleteHandler_error_title, IContextHelpIds.MESSAGE_DELETE_FAILED, this, null);
				}

				if (refreshModel) {
					// Trigger a refresh of the model
					invokeCallback = false;
					Protocol.invokeLater(new Runnable() {
						@Override
						public void run() {
							IPeerModelRefreshService service = ModelManager.getPeerModel().getService(IPeerModelRefreshService.class);
							// Refresh the model now (must be executed within the TCF dispatch thread)
							if (service != null) service.refresh(new Callback() {
								@Override
								protected void internalDone(Object caller, IStatus status) {
									// Invoke the callback
									callback.done(DeleteHandler.this, Status.OK_STATUS);
								}
							});
						}
					});
				}
			}
		}

		if (invokeCallback) {
			callback.done(this, Status.OK_STATUS);
		}
	}

	/**
	 * Analyze the given selection and convert it to an list of operations
	 * to perform.
	 *
	 * @param selection The selection. Must not be <code>null</code>.
	 * @return The list of operations.
	 */
	private Operation[] selection2operations(ITreeSelection selection) {
		Assert.isNotNull(selection);

		List<Operation> operations = new ArrayList<Operation>();

		// Iterate the selection. All elements must be of type IPeerNode
		for (TreePath treePath : selection.getPaths()) {
			// Get the element
			Object element = treePath.getLastSegment();
			Assert.isTrue(element instanceof IPeerNode);
			IPeerNode node = (IPeerNode)element;
			ICategory category = treePath.getFirstSegment() instanceof ICategory ? (ICategory)treePath.getFirstSegment() : null;

			Operation op = new Operation();
			if (category != null && IUIConstants.ID_CAT_FAVORITES.equals(category.getId())) {
				op.type = Operation.TYPE.Unlink;
			}
			else {
				op.type = Operation.TYPE.Remove;
			}
			op.node = node;
			op.parentCategory = category;
			operations.add(op);
		}

		return operations.toArray(new Operation[operations.size()]);
	}

	/**
	 * Confirm the deletion with the user.
	 *
	 * @param state The state of delegation handler.
	 * @return true if the user agrees to delete or it has confirmed previously.
	 */
	private boolean confirmDelete(Operation[] operations) {
		Assert.isNotNull(operations);

		boolean confirmed = false;

		// Find all elements to remove
		List<Operation> toRemove = new ArrayList<Operation>();
		for (Operation op : operations) {
			if (Operation.TYPE.Remove.equals(op.type)) {
				toRemove.add(op);
			}
		}

		// If there are node to remove -> ask for confirmation
		if (!toRemove.isEmpty()) {
			String question = getConfirmQuestion(toRemove);
			Shell parent = shell != null ? shell : PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
			confirmed = MessageDialog.openQuestion(parent, Messages.DeleteHandlerDelegate_DialogTitle, question);
		} else {
			confirmed = true;
		}

		return confirmed;
	}

	/**
	 * Get confirmation question displayed in the confirmation dialog.
	 *
	 * @param toRemove The list of nodes to remove.
	 * @return The question to ask the user.
	 */
	private String getConfirmQuestion(List<Operation> toRemove) {
		Assert.isNotNull(toRemove);

		String question;
		if (toRemove.size() == 1) {
			final Operation op = toRemove.get(0);
			final AtomicReference<String> name = new AtomicReference<String>();

			Runnable runnable = new Runnable() {
				@Override
				public void run() {
					name.set(op.node.getPeer().getName());
				}
			};

			if (Protocol.isDispatchThread()) {
				runnable.run();
			}
			else {
				Protocol.invokeAndWait(runnable);
			}

			question = NLS.bind(Messages.DeleteHandlerDelegate_MsgDeleteOnePeer, name.get());
		}
		else {
			question = NLS.bind(Messages.DeleteHandlerDelegate_MsgDeleteMultiplePeers, Integer.valueOf(toRemove.size()));
		}
		return question;
	}
}

Back to the top