1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.ui.actions;
15 
16 import java.util.Iterator;
17 import java.util.List;
18 
19 import org.eclipse.core.resources.IContainer;
20 import org.eclipse.core.resources.IResource;
21 import org.eclipse.core.resources.IWorkspace;
22 import org.eclipse.core.resources.IWorkspaceRoot;
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.core.runtime.IPath;
25 import org.eclipse.jface.viewers.IStructuredSelection;
26 import org.eclipse.jface.window.IShellProvider;
27 import org.eclipse.swt.widgets.Shell;
28 import org.eclipse.ui.PlatformUI;
29 import org.eclipse.ui.dialogs.ContainerSelectionDialog;
30 import org.eclipse.ui.dialogs.ISelectionValidator;
31 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
32 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
33 import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
34 
35 /**
36  * Standard action for copying the currently selected resources elsewhere
37  * in the workspace. All resources being copied as a group must be siblings.
38  * <p>
39  * This class may be instantiated; it is not intended to be subclassed.
40  * </p>
41  * @noextend This class is not intended to be subclassed by clients.
42  */
43 public class CopyResourceAction extends SelectionListenerAction implements
44 		ISelectionValidator {
45 
46 	/**
47 	 * The id of this action.
48 	 */
49 	public static final String ID = PlatformUI.PLUGIN_ID
50 			+ ".CopyResourceAction"; //$NON-NLS-1$
51 
52 	/**
53 	 * The IShellProvider in which to show any dialogs.
54 	 */
55 	protected IShellProvider shellProvider;
56 
57 	/**
58 	 * The operation to run.  This is created only during the life-cycle of the
59 	 * run method.
60 	 */
61 	protected CopyFilesAndFoldersOperation operation;
62 
63 	private String[] modelProviderIds;
64 
65 	/**
66 	 * Returns a new name for a copy of the resource at the given path in the given
67 	 * workspace. This name could be determined either automatically or by querying
68 	 * the user. This name will <b>not</b> be verified by the caller, so it must be
69 	 * valid and unique.
70 	 * <p>
71 	 * Note this method is for internal use only.
72 	 * </p>
73 	 *
74 	 * @param originalName the full path of the resource
75 	 * @param workspace the workspace
76 	 * @return the new full path for the copy, or <code>null</code> if the resource
77 	 *   should not be copied
78 	 */
getNewNameFor(IPath originalName, IWorkspace workspace)79 	public static IPath getNewNameFor(IPath originalName, IWorkspace workspace) {
80 		return CopyFilesAndFoldersOperation.getAutoNewNameFor(originalName,
81 				workspace);
82 	}
83 
84 	/**
85 	 * Creates a new action.
86 	 *
87 	 * @param shell the shell for any dialogs
88 	 *
89 	 * @deprecated {@link #CopyResourceAction(IShellProvider)}
90 	 */
91 	@Deprecated
CopyResourceAction(Shell shell)92 	public CopyResourceAction(Shell shell) {
93 		this(shell, IDEWorkbenchMessages.CopyResourceAction_title);
94 	}
95 
96 	/**
97 	 * Creates a new action
98 	 *
99 	 * @param provider the shell for any dialogs
100 	 * @since 3.4
101 	 */
CopyResourceAction(IShellProvider provider)102 	public CopyResourceAction(IShellProvider provider){
103 		this(provider, IDEWorkbenchMessages.CopyResourceAction_title);
104 	}
105 
106 	/**
107 	 * Creates a new action with the given text.
108 	 *
109 	 * @param shell the shell for any dialogs
110 	 * @param name the string used as the name for the action,
111 	 *   or <code>null</code> if there is no name
112 	 *
113 	 * @deprecated {@link #CopyResourceAction(IShellProvider, String)}
114 	 */
115 	@Deprecated
CopyResourceAction(final Shell shell, String name)116 	CopyResourceAction(final Shell shell, String name) {
117 		super(name);
118 		Assert.isNotNull(shell);
119 		shellProvider = () -> shell;
120 		initAction();
121 	}
122 
123 	/**
124 	 * Creates a new action with the given text
125 	 *
126 	 * @param provider the shell for any dialogs
127 	 * @param name the string used as the name for the action,
128 	 *   or <code>null</code> if there is no name
129 	 */
CopyResourceAction(IShellProvider provider, String name)130 	CopyResourceAction(IShellProvider provider, String name){
131 		super(name);
132 		Assert.isNotNull(provider);
133 		shellProvider = provider;
134 		initAction();
135 	}
136 
137 	/**
138 	 * Returns the operation to perform when this action runs.
139 	 *
140 	 * @return the operation to perform when this action runs.
141 	 */
createOperation()142 	protected CopyFilesAndFoldersOperation createOperation() {
143 		return new CopyFilesAndFoldersOperation(getShell());
144 	}
145 
initAction()146 	private void initAction(){
147 		setToolTipText(IDEWorkbenchMessages.CopyResourceAction_toolTip);
148 		setId(CopyResourceAction.ID);
149 		PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
150 				IIDEHelpContextIds.COPY_RESOURCE_ACTION);
151 	}
152 
153 	/**
154 	 * Returns the path of the container to initially select in the container
155 	 * selection dialog, or <code>null</code> if there is no initial selection
156 	 * @return The initial container; <code>null</code> if none.
157 	 */
getInitialContainer()158 	IContainer getInitialContainer() {
159 		List<? extends IResource> resources = getSelectedResources();
160 		if (resources.size() > 0) {
161 			IResource resource = resources.get(0);
162 			return resource.getParent();
163 		}
164 		return null;
165 	}
166 
167 	/**
168 	 * Returns an array of resources to use for the operation from
169 	 * the provided list.
170 	 *
171 	 * @param resourceList The list of resources to converted into an array.
172 	 * @return an array of resources to use for the operation
173 	 */
getResources(List<? extends IResource> resourceList)174 	protected IResource[] getResources(List<? extends IResource> resourceList) {
175 		return resourceList.toArray(new IResource[resourceList
176 				.size()]);
177 	}
178 
179 	/**
180 	 * Returns the shell in which to show any dialogs
181 	 * @return The shell for parenting dialogs; never <code>null</code>.
182 	 */
getShell()183 	Shell getShell() {
184 		return shellProvider.getShell();
185 	}
186 
187 	/**
188 	 * The <code>CopyResourceAction</code> implementation of this
189 	 * <code>ISelectionValidator</code> method checks whether the given path
190 	 * is a good place to copy the selected resources.
191 	 */
192 	@Override
isValid(Object destination)193 	public String isValid(Object destination) {
194 		IWorkspaceRoot root = IDEWorkbenchPlugin.getPluginWorkspace().getRoot();
195 		IContainer container = (IContainer) root
196 				.findMember((IPath) destination);
197 
198 		if (container != null) {
199 			// create a new operation here.
200 			// isValid is API and may be called in any context.
201 			CopyFilesAndFoldersOperation newOperation = createOperation();
202 			List<? extends IResource> sources = getSelectedResources();
203 			IResource[] resources = sources
204 					.toArray(new IResource[sources.size()]);
205 			return newOperation.validateDestination(container, resources);
206 		}
207 		return null;
208 	}
209 
210 	/**
211 	 * Asks the user for the destination of this action.
212 	 *
213 	 * @return the path on an existing or new resource container, or
214 	 *  <code>null</code> if the operation should be abandoned
215 	 */
queryDestinationResource()216 	IPath queryDestinationResource() {
217 		// start traversal at root resource, should probably start at a
218 		// better location in the tree
219 		ContainerSelectionDialog dialog = new ContainerSelectionDialog(shellProvider.getShell(),
220 				getInitialContainer(), true, IDEWorkbenchMessages.CopyResourceAction_selectDestination);
221 		dialog.setValidator(this);
222 		dialog.showClosedProjects(false);
223 		dialog.open();
224 		Object[] result = dialog.getResult();
225 		if (result != null && result.length == 1) {
226 			return (IPath) result[0];
227 		}
228 		return null;
229 	}
230 
231 	@Override
run()232 	public void run() {
233 		try {
234 			operation = createOperation();
235 			operation.setModelProviderIds(getModelProviderIds());
236 
237 			// WARNING: do not query the selected resources more than once
238 			// since the selection may change during the run,
239 			// e.g. due to window activation when the prompt dialog is dismissed.
240 			// For more details, see Bug 60606 [Navigator] (data loss) Navigator deletes/moves the wrong file
241 			List<? extends IResource> sources = getSelectedResources();
242 
243 			IPath destination = queryDestinationResource();
244 			if (destination == null) {
245 				return;
246 			}
247 
248 			IWorkspaceRoot root = IDEWorkbenchPlugin.getPluginWorkspace()
249 					.getRoot();
250 			IContainer container = (IContainer) root.findMember(destination);
251 			if (container == null) {
252 				return;
253 			}
254 
255 			runOperation(getResources(sources), container);
256 		} finally {
257 			operation = null;
258 		}
259 	}
260 
261 	/**
262 	 * Runs the operation created in <code>createOperation</code>
263 	 *
264 	 * @param resources source resources to pass to the operation
265 	 * @param destination destination container to pass to the operation
266 	 */
runOperation(IResource[] resources, IContainer destination)267 	protected void runOperation(IResource[] resources, IContainer destination) {
268 		operation.copyResources(resources, destination);
269 	}
270 
271 	/**
272 	 * The <code>CopyResourceAction</code> implementation of this
273 	 * <code>SelectionListenerAction</code> method enables this action only if
274 	 * all of the one or more selections are sibling resources which are
275 	 * local (depth infinity).
276 	 */
277 	@Override
updateSelection(IStructuredSelection selection)278 	protected boolean updateSelection(IStructuredSelection selection) {
279 		if (!super.updateSelection(selection)) {
280 			return false;
281 		}
282 		if (getSelectedNonResources().size() > 0) {
283 			return false;
284 		}
285 
286 		// to enable this command all selected resources must be siblings
287 		List<? extends IResource> selectedResources = getSelectedResources();
288 		if (selectedResources.isEmpty()) {
289 			return false;
290 		}
291 		IContainer firstParent = ((IResource) selectedResources.get(0))
292 				.getParent();
293 		if (firstParent == null) {
294 			return false;
295 		}
296 		Iterator<? extends IResource> resourcesEnum = selectedResources.iterator();
297 		while (resourcesEnum.hasNext()) {
298 			IResource currentResource = resourcesEnum.next();
299 			if (!currentResource.exists()) {
300 				return false;
301 			}
302 			if (currentResource.getType() == IResource.PROJECT) {
303 				return false;
304 			}
305 			IContainer parent = currentResource.getParent();
306 			if ((parent != null) && (!parent.equals(firstParent))) {
307 				return false;
308 			}
309 		}
310 		return true;
311 	}
312 
313 	/**
314 	 * Returns the model provider ids that are known to the client
315 	 * that instantiated this operation.
316 	 *
317 	 * @return the model provider ids that are known to the client
318 	 * that instantiated this operation.
319 	 * @since 3.2
320 	 */
getModelProviderIds()321 	public String[] getModelProviderIds() {
322 		return modelProviderIds;
323 	}
324 
325 	/**
326 	 * Sets the model provider ids that are known to the client
327 	 * that instantiated this operation. Any potential side effects
328 	 * reported by these models during validation will be ignored.
329 	 *
330 	 * @param modelProviderIds the model providers known to the client
331 	 * who is using this operation.
332 	 * @since 3.2
333 	 */
setModelProviderIds(String[] modelProviderIds)334 	public void setModelProviderIds(String[] modelProviderIds) {
335 		this.modelProviderIds = modelProviderIds;
336 	}
337 }
338