/******************************************************************************* * Copyright (c) 2012, 2016 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.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; 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.Status; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.window.Window; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.tcf.protocol.IPeer; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.te.core.interfaces.IConnectable; 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.IPersistableNodeProperties; import org.eclipse.tcf.te.runtime.persistence.interfaces.IURIPersistenceService; import org.eclipse.tcf.te.runtime.services.ServiceManager; import org.eclipse.tcf.te.runtime.statushandler.StatusHandlerUtil; import org.eclipse.tcf.te.runtime.utils.StatusHelper; import org.eclipse.tcf.te.tcf.core.peers.Peer; import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerModel; import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode; import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNodeProperties; 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.dialogs.RenameDialog; import org.eclipse.tcf.te.ui.views.ViewsUtil; import org.eclipse.tcf.te.ui.views.interfaces.IUIConstants; import org.eclipse.ui.handlers.HandlerUtil; /** * Rename handler implementation. */ public class RenameHandler 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) { rename(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); } /** * Renames 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 rename(final 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; // The selection must be a structured selection and must not be empty if (selection instanceof IStructuredSelection && !selection.isEmpty()) { Iterator<?> iterator = ((IStructuredSelection)selection).iterator(); while (iterator.hasNext()) { Object element = iterator.next(); Assert.isTrue(element instanceof IPeerNode); final IPeerNode node = (IPeerNode)element; RenameDialog dialog = createRenameDialog(shell, node); int ok = dialog.open(); if (ok == Window.OK) { // Do the renaming. final String newName = dialog.getNewName(); Runnable runnable = new Runnable() { @Override public void run() { if (newName != null && !"".equals(newName) && !newName.equals(node.getPeer().getName())) { //$NON-NLS-1$ // To update the peer attributes, the peer needs to be recreated IPeer oldPeer = node.getPeer(); // Create a write able copy of the peer attributes Map<String, String> attributes = new HashMap<String, String>(oldPeer.getAttributes()); // Update the name attributes.put(IPeer.ATTR_NAME, newName); // Remove the persistence storage URI (if set) attributes.remove(IPersistableNodeProperties.PROPERTY_URI); // Create the new peer IPeer newPeer = new Peer(attributes); try { // Get the persistence service IURIPersistenceService uRIPersistenceService = ServiceManager.getInstance().getService(IURIPersistenceService.class); if (uRIPersistenceService == null) { throw new IOException("Persistence service instance unavailable."); //$NON-NLS-1$ } // Update the peer node instance (silently) boolean changed = node.setChangeEventsEnabled(false); node.setProperty(IPeerNodeProperties.PROPERTY_INSTANCE, newPeer); if (changed) { node.setChangeEventsEnabled(true); } // Remove the old persisted peer uRIPersistenceService.delete(oldPeer, null); // Save the peer node to the new persistence storage uRIPersistenceService.write(newPeer, null); Protocol.invokeLater(new Runnable() { @Override public void run() { // Trigger a change event for the node node.fireChangeEvent("properties", null, node.getProperties()); //$NON-NLS-1$ } }); } catch (IOException e) { // Remove new peer and restore the old one IURIPersistenceService uRIPersistenceService = ServiceManager.getInstance().getService(IURIPersistenceService.class); if (uRIPersistenceService != null) { try { uRIPersistenceService.delete(newPeer, null); } catch (Exception ex) { /* Ignored on purpose */ } try { boolean changed = node.setChangeEventsEnabled(false); node.setProperty(IPeerNodeProperties.PROPERTY_INSTANCE, oldPeer); if (changed) { node.setChangeEventsEnabled(true); } Protocol.invokeLater(new Runnable() { @Override public void run() { // Trigger a change event for the node node.fireChangeEvent("properties", null, node.getProperties()); //$NON-NLS-1$ } }); uRIPersistenceService.write(oldPeer, null); } catch (Exception ex) { /* Ignored on purpose */ } } String template = NLS.bind(Messages.RenameHandler_error_renameFailed, Messages.PossibleCause); StatusHandlerUtil.handleStatus(StatusHelper.getStatus(e), selection, template, Messages.RenameHandler_error_title, IContextHelpIds.MESSAGE_RENAME_FAILED, this, null, true); } } } }; if (Protocol.isDispatchThread()) { runnable.run(); } else { Protocol.invokeAndWait(runnable); } // Trigger a refresh of the model invokeCallback = false; Protocol.invokeLater(new Runnable() { @Override public void run() { final 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(RenameHandler.this, Status.OK_STATUS); } }); } } }); } } } if (invokeCallback) { callback.done(this, Status.OK_STATUS); } } /** * Create a renaming dialog for the specified peer model node. * * @param shell The parent shell or <code>null</code> * @param node The peer model. Must not be <code>null</code>. * * @return The renaming dialog. */ private RenameDialog createRenameDialog(final Shell shell, final IPeerNode node) { Assert.isNotNull(node); final AtomicReference<String> name = new AtomicReference<String>(); final List<String> usedNames = new ArrayList<String>(); Runnable runnable = new Runnable() { @Override public void run() { name.set(node.getPeer().getName()); IPeerModel model = ModelManager.getPeerModel(); Assert.isNotNull(model); IPeerNode[] peers = model.getPeerNodes(); for (IPeerNode peer : peers) { String name = peer.getPeer().getName(); Assert.isNotNull(name); if (!"".equals(name) && !usedNames.contains(name)) { //$NON-NLS-1$ usedNames.add(name.trim().toUpperCase()); } } } }; if (Protocol.isDispatchThread()) { runnable.run(); } else { Protocol.invokeAndWait(runnable); } String title = NLS.bind(Messages.RenameHandler_dialog_title, name.get()); String prompt = Messages.RenameHandler_dialog_message; String usedError = Messages.RenameHandler_dialog_error_nameExist; String formatError = Messages.RenameHandler_dialog_error_nameFormat; String label = Messages.RenameHandler_dialog_promptNewName; return new RenameDialog(shell, null, title, prompt, usedError, formatError, label, name.get(), "[0-9a-zA-Z. _()-]+", usedNames.toArray(new String[usedNames.size()]), null); //$NON-NLS-1$ } /** * Tests if this rename handler can rename the elements of the given * <code>selection</code>. * * @param selection The selection. Must not be <code>null</code>. * @return <code>True</code> if the selection can be renamed by this handler, <code>false</code> otherwise. */ public boolean canRename(ISelection selection) { Assert.isNotNull(selection); boolean canRename = 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, must not be empty, and only a single element must be selected if (selection instanceof ITreeSelection && !selection.isEmpty() && ((ITreeSelection)selection).size() == 1) { TreePath treePath = ((ITreeSelection)selection).getPaths()[0]; // Get the element Object element = treePath.getLastSegment(); // This handler will take care of peer model nodes only if (element instanceof IPeerNode) { IPeerNode node = (IPeerNode) element; // It can be renamed only when not connected canRename = IConnectable.STATE_DISCONNECTED == node.getConnectState(); } } return canRename; } }