KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > actions > CopyFilesAndFoldersOperation


1 /*******************************************************************************
2  * Copyright (c) 2000, 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  *******************************************************************************/

11 package org.eclipse.ui.actions;
12
13 import java.io.File JavaDoc;
14 import java.lang.reflect.InvocationTargetException JavaDoc;
15 import java.net.URI JavaDoc;
16 import com.ibm.icu.text.MessageFormat;
17 import java.util.ArrayList JavaDoc;
18 import java.util.Arrays JavaDoc;
19 import java.util.List JavaDoc;
20
21 import org.eclipse.core.commands.ExecutionException;
22 import org.eclipse.core.filesystem.EFS;
23 import org.eclipse.core.filesystem.IFileInfo;
24 import org.eclipse.core.filesystem.IFileStore;
25 import org.eclipse.core.resources.IContainer;
26 import org.eclipse.core.resources.IFile;
27 import org.eclipse.core.resources.IFolder;
28 import org.eclipse.core.resources.IProject;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.resources.IWorkspace;
31 import org.eclipse.core.resources.IWorkspaceRoot;
32 import org.eclipse.core.resources.ResourcesPlugin;
33 import org.eclipse.core.resources.WorkspaceJob;
34 import org.eclipse.core.runtime.CoreException;
35 import org.eclipse.core.runtime.IAdaptable;
36 import org.eclipse.core.runtime.IPath;
37 import org.eclipse.core.runtime.IProgressMonitor;
38 import org.eclipse.core.runtime.IStatus;
39 import org.eclipse.core.runtime.MultiStatus;
40 import org.eclipse.core.runtime.OperationCanceledException;
41 import org.eclipse.core.runtime.Path;
42 import org.eclipse.core.runtime.Status;
43 import org.eclipse.core.runtime.SubProgressMonitor;
44 import org.eclipse.jface.dialogs.ErrorDialog;
45 import org.eclipse.jface.dialogs.IDialogConstants;
46 import org.eclipse.jface.dialogs.IInputValidator;
47 import org.eclipse.jface.dialogs.InputDialog;
48 import org.eclipse.jface.dialogs.MessageDialog;
49 import org.eclipse.jface.operation.IRunnableWithProgress;
50 import org.eclipse.jface.window.Window;
51 import org.eclipse.osgi.util.NLS;
52 import org.eclipse.swt.SWT;
53 import org.eclipse.swt.widgets.Display;
54 import org.eclipse.swt.widgets.Shell;
55 import org.eclipse.ui.PlatformUI;
56 import org.eclipse.ui.dialogs.IOverwriteQuery;
57 import org.eclipse.ui.ide.undo.AbstractWorkspaceOperation;
58 import org.eclipse.ui.ide.undo.CopyResourcesOperation;
59 import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
60 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
61 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
62 import org.eclipse.ui.internal.ide.StatusUtil;
63 import org.eclipse.ui.internal.ide.dialogs.IDEResourceInfoUtils;
64 import org.eclipse.ui.wizards.datatransfer.FileStoreStructureProvider;
65 import org.eclipse.ui.wizards.datatransfer.ImportOperation;
66
67 /**
68  * Perform the copy of file and folder resources from the clipboard when paste
69  * action is invoked.
70  * <p>
71  * This class may be instantiated; it is not intended to be subclassed.
72  * </p>
73  */

74 public class CopyFilesAndFoldersOperation {
75
76     /**
77      * Status containing the errors detected when running the operation or
78      * <code>null</code> if no errors detected.
79      */

80     private MultiStatus errorStatus;
81
82     /**
83      * The parent shell used to show any dialogs.
84      */

85     private Shell messageShell;
86
87     /**
88      * Whether or not the copy has been canceled by the user.
89      */

90     private boolean canceled = false;
91
92     /**
93      * Overwrite all flag.
94      */

95     private boolean alwaysOverwrite = false;
96
97     private String JavaDoc[] modelProviderIds;
98
99     /**
100      * Returns a new name for a copy of the resource at the given path in the
101      * given workspace. This name is determined automatically.
102      *
103      * @param originalName
104      * the full path of the resource
105      * @param workspace
106      * the workspace
107      * @return the new full path for the copy
108      */

109     static IPath getAutoNewNameFor(IPath originalName, IWorkspace workspace) {
110         int counter = 1;
111         String JavaDoc resourceName = originalName.lastSegment();
112         IPath leadupSegment = originalName.removeLastSegments(1);
113
114         while (true) {
115             String JavaDoc nameSegment;
116
117             if (counter > 1) {
118                 nameSegment = NLS
119                         .bind(
120                                 IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyNameTwoArgs,
121                                 new Integer JavaDoc(counter), resourceName);
122             } else {
123                 nameSegment = NLS
124                         .bind(
125                                 IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyNameOneArg,
126                                 resourceName);
127             }
128
129             IPath pathToTry = leadupSegment.append(nameSegment);
130
131             if (!workspace.getRoot().exists(pathToTry)) {
132                 return pathToTry;
133             }
134
135             counter++;
136         }
137     }
138
139     /**
140      * Creates a new operation initialized with a shell.
141      *
142      * @param shell
143      * parent shell for error dialogs
144      */

145     public CopyFilesAndFoldersOperation(Shell shell) {
146         messageShell = shell;
147     }
148
149     /**
150      * Returns whether this operation is able to perform on-the-fly
151      * auto-renaming of resources with name collisions.
152      *
153      * @return <code>true</code> if auto-rename is supported, and
154      * <code>false</code> otherwise
155      */

156     protected boolean canPerformAutoRename() {
157         return true;
158     }
159
160     /**
161      * Returns the message for querying deep copy/move of a linked resource.
162      *
163      * @param source
164      * resource the query is made for
165      * @return the deep query message
166      */

167     protected String JavaDoc getDeepCheckQuestion(IResource source) {
168         return NLS
169                 .bind(
170                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_deepCopyQuestion,
171                         source.getFullPath().makeRelative());
172     }
173
174     /**
175      * Checks whether the infos exist.
176      *
177      * @param stores
178      * the file infos to test
179      * @return Multi status with one error message for each missing file.
180      */

181     IStatus checkExist(IFileStore[] stores) {
182         MultiStatus multiStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
183                 IStatus.OK, getProblemsMessage(), null);
184
185         for (int i = 0; i < stores.length; i++) {
186             if (stores[i].fetchInfo().exists() == false) {
187                 String JavaDoc message = NLS
188                         .bind(
189                                 IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
190                                 stores[i].getName());
191                 IStatus status = new Status(IStatus.ERROR,
192                         PlatformUI.PLUGIN_ID, IStatus.OK, message, null);
193                 multiStatus.add(status);
194             }
195         }
196         return multiStatus;
197     }
198
199     /**
200      * Checks whether the resources with the given names exist.
201      *
202      * @param resources
203      * IResources to checl
204      * @return Multi status with one error message for each missing file.
205      */

206     IStatus checkExist(IResource[] resources) {
207         MultiStatus multiStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
208                 IStatus.OK, getProblemsMessage(), null);
209
210         for (int i = 0; i < resources.length; i++) {
211             IResource resource = resources[i];
212             if (resource != null) {
213                 URI JavaDoc location = resource.getLocationURI();
214                 String JavaDoc message = null;
215                 if (location != null) {
216                     IFileInfo info = IDEResourceInfoUtils.getFileInfo(location);
217                     if (info == null || info.exists() == false) {
218                         if (resource.isLinked()) {
219                             message = NLS
220                                     .bind(
221                                             IDEWorkbenchMessages.CopyFilesAndFoldersOperation_missingLinkTarget,
222                                             resource.getName());
223                         } else {
224                             message = NLS
225                                     .bind(
226                                             IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
227                                             resource.getName());
228                         }
229                     }
230                 }
231                 if (message != null) {
232                     IStatus status = new Status(IStatus.ERROR,
233                             PlatformUI.PLUGIN_ID, IStatus.OK, message, null);
234                     multiStatus.add(status);
235                 }
236             }
237         }
238         return multiStatus;
239     }
240
241     /**
242      * Check if the user wishes to overwrite the supplied resource or all
243      * resources.
244      *
245      * @param source
246      * the source resource
247      * @param destination
248      * the resource to be overwritten
249      * @return one of IDialogConstants.YES_ID, IDialogConstants.YES_TO_ALL_ID,
250      * IDialogConstants.NO_ID, IDialogConstants.CANCEL_ID indicating
251      * whether the current resource or all resources can be overwritten,
252      * or if the operation should be canceled.
253      */

254     private int checkOverwrite(final IResource source,
255             final IResource destination) {
256         final int[] result = new int[1];
257
258         // Dialogs need to be created and opened in the UI thread
259
Runnable JavaDoc query = new Runnable JavaDoc() {
260             public void run() {
261                 String JavaDoc message;
262                 int resultId[] = { IDialogConstants.YES_ID,
263                         IDialogConstants.YES_TO_ALL_ID, IDialogConstants.NO_ID,
264                         IDialogConstants.CANCEL_ID };
265                 String JavaDoc labels[] = new String JavaDoc[] { IDialogConstants.YES_LABEL,
266                         IDialogConstants.YES_TO_ALL_LABEL,
267                         IDialogConstants.NO_LABEL,
268                         IDialogConstants.CANCEL_LABEL };
269
270                 if (destination.getType() == IResource.FOLDER) {
271                     if (homogenousResources(source, destination)) {
272                         message = NLS
273                                 .bind(
274                                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteMergeQuestion,
275                                         destination.getFullPath()
276                                                 .makeRelative());
277                     } else {
278                         if (destination.isLinked()) {
279                             message = NLS
280                                     .bind(
281                                             IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteNoMergeLinkQuestion,
282                                             destination.getFullPath()
283                                                     .makeRelative());
284                         } else {
285                             message = NLS
286                                     .bind(
287                                             IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteNoMergeNoLinkQuestion,
288                                             destination.getFullPath()
289                                                     .makeRelative());
290                         }
291                         resultId = new int[] { IDialogConstants.YES_ID,
292                                 IDialogConstants.NO_ID,
293                                 IDialogConstants.CANCEL_ID };
294                         labels = new String JavaDoc[] { IDialogConstants.YES_LABEL,
295                                 IDialogConstants.NO_LABEL,
296                                 IDialogConstants.CANCEL_LABEL };
297                     }
298                 } else {
299                     String JavaDoc[] bindings = new String JavaDoc[] {
300                             IDEResourceInfoUtils.getLocationText(destination),
301                             IDEResourceInfoUtils
302                                     .getDateStringValue(destination),
303                             IDEResourceInfoUtils.getLocationText(source),
304                             IDEResourceInfoUtils.getDateStringValue(source) };
305                     message = NLS
306                             .bind(
307                                     IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteWithDetailsQuestion,
308                                     bindings);
309                 }
310                 MessageDialog dialog = new MessageDialog(
311                         messageShell,
312                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceExists,
313                         null, message, MessageDialog.QUESTION, labels, 0);
314                 dialog.open();
315                 if (dialog.getReturnCode() == SWT.DEFAULT) {
316                     // A window close returns SWT.DEFAULT, which has to be
317
// mapped to a cancel
318
result[0] = IDialogConstants.CANCEL_ID;
319                 } else {
320                     result[0] = resultId[dialog.getReturnCode()];
321                 }
322             }
323         };
324         messageShell.getDisplay().syncExec(query);
325         return result[0];
326     }
327
328     /**
329      * Recursively collects existing files in the specified destination path.
330      *
331      * @param destinationPath
332      * destination path to check for existing files
333      * @param copyResources
334      * resources that may exist in the destination
335      * @param existing
336      * holds the collected existing files
337      */

338     private void collectExistingReadonlyFiles(IPath destinationPath,
339             IResource[] copyResources, ArrayList JavaDoc existing) {
340         IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
341
342         for (int i = 0; i < copyResources.length; i++) {
343             IResource source = copyResources[i];
344             IPath newDestinationPath = destinationPath.append(source.getName());
345             IResource newDestination = workspaceRoot
346                     .findMember(newDestinationPath);
347             IFolder folder;
348
349             if (newDestination == null) {
350                 continue;
351             }
352             folder = getFolder(newDestination);
353             if (folder != null) {
354                 IFolder sourceFolder = getFolder(source);
355
356                 if (sourceFolder != null) {
357                     try {
358                         collectExistingReadonlyFiles(newDestinationPath,
359                                 sourceFolder.members(), existing);
360                     } catch (CoreException exception) {
361                         recordError(exception);
362                     }
363                 }
364             } else {
365                 IFile file = getFile(newDestination);
366
367                 if (file != null) {
368                     if (file.isReadOnly()) {
369                         existing.add(file);
370                     }
371                     if (getValidateConflictSource()) {
372                         IFile sourceFile = getFile(source);
373                         if (sourceFile != null) {
374                             existing.add(sourceFile);
375                         }
376                     }
377                 }
378             }
379         }
380     }
381
382     /**
383      * Copies the resources to the given destination. This method is called
384      * recursively to merge folders during folder copy.
385      *
386      * @param resources
387      * the resources to copy
388      * @param destination
389      * destination to which resources will be copied
390      * @param subMonitor
391      * a progress monitor for showing progress and for cancelation
392      *
393      * @deprecated As of 3.3, the work is performed in the undoable operation
394      * created in
395      * {@link #getUndoableCopyOrMoveOperation(IResource[], IPath)}
396      */

397     protected void copy(IResource[] resources, IPath destination,
398             IProgressMonitor subMonitor) throws CoreException {
399
400         subMonitor
401                 .beginTask(
402                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_CopyResourcesTask,
403                         resources.length);
404
405         for (int i = 0; i < resources.length; i++) {
406             IResource source = resources[i];
407             IPath destinationPath = destination.append(source.getName());
408             IWorkspace workspace = source.getWorkspace();
409             IWorkspaceRoot workspaceRoot = workspace.getRoot();
410             IResource existing = workspaceRoot.findMember(destinationPath);
411             if (source.getType() == IResource.FOLDER && existing != null) {
412                 // the resource is a folder and it exists in the destination,
413
// copy the
414
// children of the folder.
415
if (homogenousResources(source, existing)) {
416                     IResource[] children = ((IContainer) source).members();
417                     copy(children, destinationPath, new SubProgressMonitor(
418                             subMonitor, 1));
419                 } else {
420                     // delete the destination folder, copying a linked folder
421
// over an unlinked one or vice versa. Fixes bug 28772.
422
delete(existing, new SubProgressMonitor(subMonitor, 0));
423                     source.copy(destinationPath, IResource.SHALLOW,
424                             new SubProgressMonitor(subMonitor, 1));
425                 }
426             } else {
427                 if (existing != null) {
428                     if (homogenousResources(source, existing)) {
429                         copyExisting(source, existing, new SubProgressMonitor(
430                                 subMonitor, 1));
431                     } else {
432                         // Copying a linked resource over unlinked or vice
433
// versa.
434
// Can't use setContents here. Fixes bug 28772.
435
delete(existing, new SubProgressMonitor(subMonitor, 0));
436                         source.copy(destinationPath, IResource.SHALLOW,
437                                 new SubProgressMonitor(subMonitor, 1));
438                     }
439                 } else {
440                     source.copy(destinationPath, IResource.SHALLOW,
441                             new SubProgressMonitor(subMonitor, 1));
442
443                 }
444
445                 if (subMonitor.isCanceled()) {
446                     throw new OperationCanceledException();
447                 }
448             }
449         }
450     }
451
452     /**
453      * Sets the content of the existing file to the source file content.
454      *
455      * @param source
456      * source file to copy
457      * @param existing
458      * existing file to set the source content in
459      * @param subMonitor
460      * a progress monitor for showing progress and for cancelation
461      * @throws CoreException
462      * setContents failed
463      */

464     private void copyExisting(IResource source, IResource existing,
465             IProgressMonitor subMonitor) throws CoreException {
466         IFile existingFile = getFile(existing);
467
468         if (existingFile != null) {
469             IFile sourceFile = getFile(source);
470
471             if (sourceFile != null) {
472                 existingFile.setContents(sourceFile.getContents(),
473                         IResource.KEEP_HISTORY, new SubProgressMonitor(
474                                 subMonitor, 0));
475             }
476         }
477     }
478
479     /**
480      * Copies the given resources to the destination. The current Thread is
481      * halted while the resources are copied using a WorkspaceModifyOperation.
482      * This method should be called from the UIThread.
483      *
484      * @param resources
485      * the resources to copy
486      * @param destination
487      * destination to which resources will be copied
488      * @return IResource[] the resulting {@link IResource}[]
489      * @see WorkspaceModifyOperation
490      * @see Display#getThread()
491      * @see Thread#currentThread()
492      */

493     public IResource[] copyResources(final IResource[] resources,
494             IContainer destination) {
495         return copyResources(resources, destination, true, null);
496     }
497
498     /**
499      * Copies the given resources to the destination in the current Thread
500      * without forking a new Thread or blocking using a
501      * WorkspaceModifyOperation. It recommended that this method only be called
502      * from a {@link WorkspaceJob} to avoid possible deadlock.
503      *
504      * @param resources
505      * the resources to copy
506      * @param destination
507      * destination to which resources will be copied
508      * @param monitor
509      * the monitor that information will be sent to.
510      * @return IResource[] the resulting {@link IResource}[]
511      * @see WorkspaceModifyOperation
512      * @see WorkspaceJob
513      * @since 3.2
514      */

515     public IResource[] copyResourcesInCurrentThread(
516             final IResource[] resources, IContainer destination,
517             IProgressMonitor monitor) {
518         return copyResources(resources, destination, false, monitor);
519     }
520
521     /**
522      * Copies the given resources to the destination.
523      *
524      * @param resources
525      * the resources to copy
526      * @param destination
527      * destination to which resources will be copied
528      * @return IResource[] the resulting {@link IResource}[]
529      */

530     private IResource[] copyResources(final IResource[] resources,
531             IContainer destination, boolean fork, IProgressMonitor monitor) {
532         final IPath destinationPath = destination.getFullPath();
533         final IResource[][] copiedResources = new IResource[1][0];
534
535         // test resources for existence separate from validate API.
536
// Validate is performance critical and resource exists
537
// check is potentially slow. Fixes bugs 16129/28602.
538
IStatus resourceStatus = checkExist(resources);
539         if (resourceStatus.getSeverity() != IStatus.OK) {
540             displayError(resourceStatus);
541             return copiedResources[0];
542         }
543         String JavaDoc errorMsg = validateDestination(destination, resources);
544         if (errorMsg != null) {
545             displayError(errorMsg);
546             return copiedResources[0];
547         }
548
549         IRunnableWithProgress op = new IRunnableWithProgress() {
550             public void run(IProgressMonitor monitor) {
551                 copyResources(resources, destinationPath, copiedResources,
552                         monitor);
553             }
554         };
555
556         try {
557             PlatformUI.getWorkbench().getProgressService().run(fork, true, op);
558         } catch (InterruptedException JavaDoc e) {
559             return copiedResources[0];
560         } catch (InvocationTargetException JavaDoc e) {
561             display(e);
562         }
563
564         // If errors occurred, open an Error dialog
565
if (errorStatus != null) {
566             displayError(errorStatus);
567             errorStatus = null;
568         }
569
570         return copiedResources[0];
571     }
572
573     /**
574      * Return whether the operation is a move or a copy
575      *
576      * @return whether the operation is a move or a copy
577      * @since 3.2
578      */

579     protected boolean isMove() {
580         return false;
581     }
582
583     private void display(InvocationTargetException JavaDoc e) {
584         // CoreExceptions are collected above, but unexpected runtime
585
// exceptions and errors may still occur.
586
IDEWorkbenchPlugin.getDefault().getLog().log(
587                 StatusUtil.newStatus(IStatus.ERROR, MessageFormat.format(
588                         "Exception in {0}.performCopy(): {1}", //$NON-NLS-1$
589
new Object JavaDoc[] { getClass().getName(),
590                                 e.getTargetException() }), null));
591         displayError(NLS
592                 .bind(
593                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
594                         e.getTargetException().getMessage()));
595     }
596
597     /**
598      * Copies the given URIS and folders to the destination. The current Thread
599      * is halted while the resources are copied using a
600      * WorkspaceModifyOperation. This method should be called from the UI
601      * Thread.
602      *
603      * @param uris
604      * the URIs to copy
605      * @param destination
606      * destination to which files will be copied
607      * @see WorkspaceModifyOperation
608      * @see Display#getThread()
609      * @see Thread#currentThread()
610      * @since 3.2
611      */

612     public void copyFiles(URI JavaDoc[] uris, IContainer destination) {
613         IFileStore[] stores = buildFileStores(uris);
614         if (stores == null) {
615             return;
616         }
617
618         copyFileStores(destination, stores, true, null);
619     }
620
621     /**
622      * Copies the given files and folders to the destination without forking a
623      * new Thread or blocking using a WorkspaceModifyOperation. It is
624      * recommended that this method only be called from a {@link WorkspaceJob}
625      * to avoid possible deadlock.
626      *
627      * @param uris
628      * the URIs to copy
629      * @param destination
630      * destination to which URIS will be copied
631      * @param monitor
632      * the monitor that information will be sent to.
633      * @see WorkspaceModifyOperation
634      * @see WorkspaceJob
635      * @since 3.2
636      */

637     public void copyFilesInCurrentThread(URI JavaDoc[] uris, IContainer destination,
638             IProgressMonitor monitor) {
639         IFileStore[] stores = buildFileStores(uris);
640         if (stores == null) {
641             return;
642         }
643
644         copyFileStores(destination, stores, false, monitor);
645     }
646
647     /**
648      * Build the collection of fileStores that map to fileNames. If any of them
649      * cannot be found then match then return <code>null</code>.
650      *
651      * @param uris
652      * @return IFileStore[]
653      */

654     private IFileStore[] buildFileStores(URI JavaDoc[] uris) {
655         IFileStore[] stores = new IFileStore[uris.length];
656         for (int i = 0; i < uris.length; i++) {
657             IFileStore store;
658             try {
659                 store = EFS.getStore(uris[i]);
660             } catch (CoreException e) {
661                 IDEWorkbenchPlugin.log(e.getMessage(), e);
662                 reportFileInfoNotFound(uris[i].toString());
663                 return null;
664             }
665             if (store == null) {
666                 reportFileInfoNotFound(uris[i].toString());
667                 return null;
668             }
669             stores[i] = store;
670         }
671         return stores;
672
673     }
674
675     /**
676      * Copies the given files and folders to the destination. The current Thread
677      * is halted while the resources are copied using a
678      * WorkspaceModifyOperation. This method should be called from the UI
679      * Thread.
680      *
681      * @param fileNames
682      * names of the files to copy
683      * @param destination
684      * destination to which files will be copied
685      * @see WorkspaceModifyOperation
686      * @see Display#getThread()
687      * @see Thread#currentThread()
688      * @since 3.2
689      */

690     public void copyFiles(final String JavaDoc[] fileNames, IContainer destination) {
691         IFileStore[] stores = buildFileStores(fileNames);
692         if (stores == null) {
693             return;
694         }
695
696         copyFileStores(destination, stores, true, null);
697     }
698
699     /**
700      * Copies the given files and folders to the destination without forking a
701      * new Thread or blocking using a WorkspaceModifyOperation. It is
702      * recommended that this method only be called from a {@link WorkspaceJob}
703      * to avoid possible deadlock.
704      *
705      * @param fileNames
706      * names of the files to copy
707      * @param destination
708      * destination to which files will be copied
709      * @param monitor
710      * the monitor that information will be sent to.
711      * @see WorkspaceModifyOperation
712      * @see WorkspaceJob
713      * @since 3.2
714      */

715     public void copyFilesInCurrentThread(final String JavaDoc[] fileNames,
716             IContainer destination, IProgressMonitor monitor) {
717         IFileStore[] stores = buildFileStores(fileNames);
718         if (stores == null) {
719             return;
720         }
721
722         copyFileStores(destination, stores, false, monitor);
723     }
724
725     /**
726      * Build the collection of fileStores that map to fileNames. If any of them
727      * cannot be found then match then return null.
728      *
729      * @param fileNames
730      * @return IFileStore[]
731      */

732     private IFileStore[] buildFileStores(final String JavaDoc[] fileNames) {
733         IFileStore[] stores = new IFileStore[fileNames.length];
734         for (int i = 0; i < fileNames.length; i++) {
735             IFileStore store = IDEResourceInfoUtils.getFileStore(fileNames[i]);
736             if (store == null) {
737                 reportFileInfoNotFound(fileNames[i]);
738                 return null;
739             }
740             stores[i] = store;
741         }
742         return stores;
743     }
744
745     /**
746      * Report that a file info could not be found.
747      *
748      * @param fileName
749      */

750     private void reportFileInfoNotFound(final String JavaDoc fileName) {
751
752         messageShell.getDisplay().syncExec(new Runnable JavaDoc() {
753             public void run() {
754                 ErrorDialog
755                         .openError(
756                                 messageShell,
757                                 getProblemsTitle(),
758                                 NLS
759                                         .bind(
760                                                 IDEWorkbenchMessages.CopyFilesAndFoldersOperation_infoNotFound,
761                                                 fileName), null);
762             }
763         });
764     }
765
766     /**
767      * Copies the given files and folders to the destination.
768      *
769      * @param stores
770      * the file stores to copy
771      * @param destination
772      * destination to which files will be copied
773      */

774     private void copyFileStores(IContainer destination,
775             final IFileStore[] stores, boolean fork, IProgressMonitor monitor) {
776         // test files for existence separate from validate API
777
// because an external file may not exist until the copy actually
778
// takes place (e.g., WinZip contents).
779
IStatus fileStatus = checkExist(stores);
780         if (fileStatus.getSeverity() != IStatus.OK) {
781             displayError(fileStatus);
782             return;
783         }
784         String JavaDoc errorMsg = validateImportDestinationInternal(destination, stores);
785         if (errorMsg != null) {
786             displayError(errorMsg);
787             return;
788         }
789         final IPath destinationPath = destination.getFullPath();
790
791         if (fork) {
792             WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
793                 public void execute(IProgressMonitor monitor) {
794                     copyFileStores(stores, destinationPath, monitor);
795                 }
796             };
797             try {
798                 PlatformUI.getWorkbench().getProgressService().run(true, true,
799                         op);
800             } catch (InterruptedException JavaDoc e) {
801                 return;
802             } catch (InvocationTargetException JavaDoc exception) {
803                 display(exception);
804             }
805         } else {
806             copyFileStores(stores, destinationPath, monitor);
807         }
808
809         // If errors occurred, open an Error dialog
810
if (errorStatus != null) {
811             displayError(errorStatus);
812             errorStatus = null;
813         }
814     }
815
816     /**
817      * Display the supplied status in an error dialog.
818      *
819      * @param status
820      * The status to display
821      */

822     private void displayError(final IStatus status) {
823         messageShell.getDisplay().syncExec(new Runnable JavaDoc() {
824             public void run() {
825                 ErrorDialog.openError(messageShell, getProblemsTitle(), null,
826                         status);
827             }
828         });
829     }
830
831     /**
832      * Creates a file or folder handle for the source resource as if it were to
833      * be created in the destination container.
834      *
835      * @param destination
836      * destination container
837      * @param source
838      * source resource
839      * @return IResource file or folder handle, depending on the source type.
840      */

841     IResource createLinkedResourceHandle(IContainer destination,
842             IResource source) {
843         IWorkspace workspace = destination.getWorkspace();
844         IWorkspaceRoot workspaceRoot = workspace.getRoot();
845         IPath linkPath = destination.getFullPath().append(source.getName());
846         IResource linkHandle;
847
848         if (source.getType() == IResource.FOLDER) {
849             linkHandle = workspaceRoot.getFolder(linkPath);
850         } else {
851             linkHandle = workspaceRoot.getFile(linkPath);
852         }
853         return linkHandle;
854     }
855
856     /**
857      * Removes the given resource from the workspace.
858      *
859      * @param resource
860      * resource to remove from the workspace
861      * @param monitor
862      * a progress monitor for showing progress and for cancelation
863      * @return true the resource was deleted successfully false the resource was
864      * not deleted because a CoreException occurred
865      */

866     boolean delete(IResource resource, IProgressMonitor monitor) {
867         boolean force = false; // don't force deletion of out-of-sync resources
868

869         if (resource.getType() == IResource.PROJECT) {
870             // if it's a project, ask whether content should be deleted too
871
IProject project = (IProject) resource;
872             try {
873                 project.delete(true, force, monitor);
874             } catch (CoreException e) {
875                 recordError(e); // log error
876
return false;
877             }
878         } else {
879             // if it's not a project, just delete it
880
int flags = IResource.KEEP_HISTORY;
881             if (force) {
882                 flags = flags | IResource.FORCE;
883             }
884             try {
885                 resource.delete(flags, monitor);
886             } catch (CoreException e) {
887                 recordError(e); // log error
888
return false;
889             }
890         }
891         return true;
892     }
893
894     /**
895      * Opens an error dialog to display the given message.
896      *
897      * @param message
898      * the error message to show
899      */

900     private void displayError(final String JavaDoc message) {
901         messageShell.getDisplay().syncExec(new Runnable JavaDoc() {
902             public void run() {
903                 MessageDialog.openError(messageShell, getProblemsTitle(),
904                         message);
905             }
906         });
907     }
908
909     /**
910      * Returns the resource either casted to or adapted to an IFile.
911      *
912      * @param resource
913      * resource to cast/adapt
914      * @return the resource either casted to or adapted to an IFile.
915      * <code>null</code> if the resource does not adapt to IFile
916      */

917     protected IFile getFile(IResource resource) {
918         if (resource instanceof IFile) {
919             return (IFile) resource;
920         }
921         return (IFile) ((IAdaptable) resource).getAdapter(IFile.class);
922     }
923
924     /**
925      * Returns java.io.File objects for the given file names.
926      *
927      * @param fileNames
928      * files to return File object for.
929      * @return java.io.File objects for the given file names.
930      * @deprecated As of 3.3, this method is no longer in use anywhere in this
931      * class and is only provided for backwards compatability with
932      * subclasses of the receiver.
933      */

934     protected File JavaDoc[] getFiles(String JavaDoc[] fileNames) {
935         File JavaDoc[] files = new File JavaDoc[fileNames.length];
936
937         for (int i = 0; i < fileNames.length; i++) {
938             files[i] = new File JavaDoc(fileNames[i]);
939         }
940         return files;
941     }
942
943     /**
944      * Returns the resource either casted to or adapted to an IFolder.
945      *
946      * @param resource
947      * resource to cast/adapt
948      * @return the resource either casted to or adapted to an IFolder.
949      * <code>null</code> if the resource does not adapt to IFolder
950      */

951     protected IFolder getFolder(IResource resource) {
952         if (resource instanceof IFolder) {
953             return (IFolder) resource;
954         }
955         return (IFolder) ((IAdaptable) resource).getAdapter(IFolder.class);
956     }
957
958     /**
959      * Returns a new name for a copy of the resource at the given path in the
960      * given workspace.
961      *
962      * @param originalName
963      * the full path of the resource
964      * @param workspace
965      * the workspace
966      * @return the new full path for the copy, or <code>null</code> if the
967      * resource should not be copied
968      */

969     private IPath getNewNameFor(final IPath originalName,
970             final IWorkspace workspace) {
971         final IResource resource = workspace.getRoot().findMember(originalName);
972         final IPath prefix = resource.getFullPath().removeLastSegments(1);
973         final String JavaDoc returnValue[] = { "" }; //$NON-NLS-1$
974

975         messageShell.getDisplay().syncExec(new Runnable JavaDoc() {
976             public void run() {
977                 IInputValidator validator = new IInputValidator() {
978                     public String JavaDoc isValid(String JavaDoc string) {
979                         if (resource.getName().equals(string)) {
980                             return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameMustBeDifferent;
981                         }
982                         IStatus status = workspace.validateName(string,
983                                 resource.getType());
984                         if (!status.isOK()) {
985                             return status.getMessage();
986                         }
987                         if (workspace.getRoot().exists(prefix.append(string))) {
988                             return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameExists;
989                         }
990                         return null;
991                     }
992                 };
993
994                 InputDialog dialog = new InputDialog(
995                         messageShell,
996                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_inputDialogTitle,
997                         NLS
998                                 .bind(
999                                         IDEWorkbenchMessages.CopyFilesAndFoldersOperation_inputDialogMessage,
1000                                        resource.getName()), getAutoNewNameFor(
1001                                originalName, workspace).lastSegment()
1002                                .toString(), validator);
1003                dialog.setBlockOnOpen(true);
1004                dialog.open();
1005                if (dialog.getReturnCode() == Window.CANCEL) {
1006                    returnValue[0] = null;
1007                } else {
1008                    returnValue[0] = dialog.getValue();
1009                }
1010            }
1011        });
1012        if (returnValue[0] == null) {
1013            throw new OperationCanceledException();
1014        }
1015        return prefix.append(returnValue[0]);
1016    }
1017
1018    /**
1019     * Returns the task title for this operation's progress dialog.
1020     *
1021     * @return the task title
1022     */

1023    protected String JavaDoc getOperationTitle() {
1024        return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_operationTitle;
1025    }
1026
1027    /**
1028     * Returns the message for this operation's problems dialog.
1029     *
1030     * @return the problems message
1031     */

1032    protected String JavaDoc getProblemsMessage() {
1033        return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_problemMessage;
1034    }
1035
1036    /**
1037     * Returns the title for this operation's problems dialog.
1038     *
1039     * @return the problems dialog title
1040     */

1041    protected String JavaDoc getProblemsTitle() {
1042        return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyFailedTitle;
1043    }
1044
1045    /**
1046     * Returns whether the source file in a destination collision will be
1047     * validateEdited together with the collision itself. Returns false. Should
1048     * return true if the source file is to be deleted after the operation.
1049     *
1050     * @return boolean <code>true</code> if the source file in a destination
1051     * collision should be validateEdited. <code>false</code> if only
1052     * the destination should be validated.
1053     */

1054    protected boolean getValidateConflictSource() {
1055        return false;
1056    }
1057
1058    /**
1059     * Returns whether the given resources are either both linked or both
1060     * unlinked.
1061     *
1062     * @param source
1063     * source resource
1064     * @param destination
1065     * destination resource
1066     * @return boolean <code>true</code> if both resources are either linked
1067     * or unlinked. <code>false</code> otherwise.
1068     */

1069    protected boolean homogenousResources(IResource source,
1070            IResource destination) {
1071        boolean isSourceLinked = source.isLinked();
1072        boolean isDestinationLinked = destination.isLinked();
1073
1074        return (isSourceLinked && isDestinationLinked || isSourceLinked == false
1075                && isDestinationLinked == false);
1076    }
1077
1078    /**
1079     * Returns whether the given resource is accessible. Files and folders are
1080     * always considered accessible and a project is accessible if it is open.
1081     *
1082     * @param resource
1083     * the resource
1084     * @return <code>true</code> if the resource is accessible, and
1085     * <code>false</code> if it is not
1086     */

1087    private boolean isAccessible(IResource resource) {
1088        switch (resource.getType()) {
1089        case IResource.FILE:
1090            return true;
1091        case IResource.FOLDER:
1092            return true;
1093        case IResource.PROJECT:
1094            return ((IProject) resource).isOpen();
1095        default:
1096            return false;
1097        }
1098    }
1099
1100    /**
1101     * Returns whether any of the given source resources are being recopied to
1102     * their current container.
1103     *
1104     * @param sourceResources
1105     * the source resources
1106     * @param destination
1107     * the destination container
1108     * @return <code>true</code> if at least one of the given source
1109     * resource's parent container is the same as the destination
1110     */

1111    boolean isDestinationSameAsSource(IResource[] sourceResources,
1112            IContainer destination) {
1113        IPath destinationLocation = destination.getLocation();
1114
1115        for (int i = 0; i < sourceResources.length; i++) {
1116            IResource sourceResource = sourceResources[i];
1117            if (sourceResource.getParent().equals(destination)) {
1118                return true;
1119            } else if (destinationLocation != null) {
1120                // do thorough check to catch linked resources. Fixes bug 29913.
1121
IPath sourceLocation = sourceResource.getLocation();
1122                IPath destinationResource = destinationLocation
1123                        .append(sourceResource.getName());
1124                if (sourceLocation != null
1125                        && sourceLocation.isPrefixOf(destinationResource)) {
1126                    return true;
1127                }
1128            }
1129        }
1130        return false;
1131    }
1132
1133    /**
1134     * Copies the given resources to the destination container with the given
1135     * name.
1136     * <p>
1137     * Note: the destination container may need to be created prior to copying
1138     * the resources.
1139     * </p>
1140     *
1141     * @param resources
1142     * the resources to copy
1143     * @param destination
1144     * the path of the destination container
1145     * @param monitor
1146     * a progress monitor for showing progress and for cancelation
1147     * @return <code>true</code> if the copy operation completed without
1148     * errors
1149     */

1150    private boolean performCopy(IResource[] resources, IPath destination,
1151            IProgressMonitor monitor) {
1152        try {
1153            AbstractWorkspaceOperation op = getUndoableCopyOrMoveOperation(
1154                    resources, destination);
1155            op.setModelProviderIds(getModelProviderIds());
1156            PlatformUI.getWorkbench().getOperationSupport()
1157                    .getOperationHistory().execute(op, monitor,
1158                            WorkspaceUndoUtil.getUIInfoAdapter(messageShell));
1159        } catch (ExecutionException e) {
1160            if (e.getCause() instanceof CoreException) {
1161                recordError((CoreException) e.getCause());
1162            } else {
1163                IDEWorkbenchPlugin.log(e.getMessage(), e);
1164                displayError(e.getMessage());
1165            }
1166            return false;
1167        }
1168        return true;
1169    }
1170
1171    /**
1172     * Individually copies the given resources to the specified destination
1173     * container checking for name collisions. If a collision is detected, it is
1174     * saved with a new name.
1175     * <p>
1176     * Note: the destination container may need to be created prior to copying
1177     * the resources.
1178     * </p>
1179     *
1180     * @param resources
1181     * the resources to copy
1182     * @param destination
1183     * the path of the destination container
1184     * @return <code>true</code> if the copy operation completed without
1185     * errors.
1186     */

1187    private boolean performCopyWithAutoRename(IResource[] resources,
1188            IPath destination, IProgressMonitor monitor) {
1189        IWorkspace workspace = resources[0].getWorkspace();
1190        IPath[] destinationPaths = new IPath[resources.length];
1191        try {
1192            for (int i = 0; i < resources.length; i++) {
1193                IResource source = resources[i];
1194                destinationPaths[i] = destination.append(source.getName());
1195
1196                if (workspace.getRoot().exists(destinationPaths[i])) {
1197                    destinationPaths[i] = getNewNameFor(destinationPaths[i],
1198                            workspace);
1199                }
1200            }
1201            CopyResourcesOperation op = new CopyResourcesOperation(resources,
1202                    destinationPaths,
1203                    IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyTitle);
1204            op.setModelProviderIds(getModelProviderIds());
1205            PlatformUI.getWorkbench().getOperationSupport()
1206                    .getOperationHistory().execute(op, monitor,
1207                            WorkspaceUndoUtil.getUIInfoAdapter(messageShell));
1208        } catch (ExecutionException e) {
1209            if (e.getCause() instanceof CoreException) {
1210                recordError((CoreException) e.getCause());
1211            } else {
1212                IDEWorkbenchPlugin.log(e.getMessage(), e);
1213                displayError(e.getMessage());
1214            }
1215            return false;
1216        }
1217        return true;
1218    }
1219
1220    /**
1221     * Performs an import of the given stores into the provided container.
1222     * Returns a status indicating if the import was successful.
1223     *
1224     * @param stores
1225     * stores that are to be imported
1226     * @param target
1227     * container to which the import will be done
1228     * @param monitor
1229     * a progress monitor for showing progress and for cancelation
1230     */

1231    private void performFileImport(IFileStore[] stores, IContainer target,
1232            IProgressMonitor monitor) {
1233        IOverwriteQuery query = new IOverwriteQuery() {
1234            public String JavaDoc queryOverwrite(String JavaDoc pathString) {
1235                if (alwaysOverwrite) {
1236                    return ALL;
1237                }
1238
1239                final String JavaDoc returnCode[] = { CANCEL };
1240                final String JavaDoc msg = NLS
1241                        .bind(
1242                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteQuestion,
1243                                pathString);
1244                final String JavaDoc[] options = { IDialogConstants.YES_LABEL,
1245                        IDialogConstants.YES_TO_ALL_LABEL,
1246                        IDialogConstants.NO_LABEL,
1247                        IDialogConstants.CANCEL_LABEL };
1248                messageShell.getDisplay().syncExec(new Runnable JavaDoc() {
1249                    public void run() {
1250                        MessageDialog dialog = new MessageDialog(
1251                                messageShell,
1252                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_question,
1253                                null, msg, MessageDialog.QUESTION, options, 0);
1254                        dialog.open();
1255                        int returnVal = dialog.getReturnCode();
1256                        String JavaDoc[] returnCodes = { YES, ALL, NO, CANCEL };
1257                        returnCode[0] = returnVal == -1 ? CANCEL
1258                                : returnCodes[returnVal];
1259                    }
1260                });
1261                if (returnCode[0] == ALL) {
1262                    alwaysOverwrite = true;
1263                } else if (returnCode[0] == CANCEL) {
1264                    canceled = true;
1265                }
1266                return returnCode[0];
1267            }
1268        };
1269
1270        ImportOperation op = new ImportOperation(target.getFullPath(),
1271                stores[0].getParent(), FileStoreStructureProvider.INSTANCE,
1272                query, Arrays.asList(stores));
1273        op.setContext(messageShell);
1274        op.setCreateContainerStructure(false);
1275        try {
1276            op.run(monitor);
1277        } catch (InterruptedException JavaDoc e) {
1278            return;
1279        } catch (InvocationTargetException JavaDoc e) {
1280            if (e.getTargetException() instanceof CoreException) {
1281                displayError(((CoreException) e.getTargetException())
1282                        .getStatus());
1283            } else {
1284                display(e);
1285            }
1286            return;
1287        }
1288        // Special case since ImportOperation doesn't throw a CoreException on
1289
// failure.
1290
IStatus status = op.getStatus();
1291        if (!status.isOK()) {
1292            if (errorStatus == null) {
1293                errorStatus = new MultiStatus(PlatformUI.PLUGIN_ID,
1294                        IStatus.ERROR, getProblemsMessage(), null);
1295            }
1296            errorStatus.merge(status);
1297        }
1298    }
1299
1300    /**
1301     * Records the core exception to be displayed to the user once the action is
1302     * finished.
1303     *
1304     * @param error
1305     * a <code>CoreException</code>
1306     */

1307    private void recordError(CoreException error) {
1308        if (errorStatus == null) {
1309            errorStatus = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.ERROR,
1310                    getProblemsMessage(), error);
1311        }
1312
1313        errorStatus.merge(error.getStatus());
1314    }
1315
1316    /**
1317     * Checks whether the destination is valid for copying the source resources.
1318     * <p>
1319     * Note this method is for internal use only. It is not API.
1320     * </p>
1321     *
1322     * @param destination
1323     * the destination container
1324     * @param sourceResources
1325     * the source resources
1326     * @return an error message, or <code>null</code> if the path is valid
1327     */

1328    public String JavaDoc validateDestination(IContainer destination,
1329            IResource[] sourceResources) {
1330        if (!isAccessible(destination)) {
1331            return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationAccessError;
1332        }
1333        IContainer firstParent = null;
1334        URI JavaDoc destinationLocation = destination.getLocationURI();
1335        for (int i = 0; i < sourceResources.length; i++) {
1336            IResource sourceResource = sourceResources[i];
1337            if (firstParent == null) {
1338                firstParent = sourceResource.getParent();
1339            } else if (firstParent.equals(sourceResource.getParent()) == false) {
1340                // Resources must have common parent. Fixes bug 33398.
1341
return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_parentNotEqual;
1342            }
1343
1344            URI JavaDoc sourceLocation = sourceResource.getLocationURI();
1345            if (sourceLocation == null) {
1346                if (sourceResource.isLinked()) {
1347                    // Don't allow copying linked resources with undefined path
1348
// variables. See bug 28754.
1349
return NLS
1350                            .bind(
1351                                    IDEWorkbenchMessages.CopyFilesAndFoldersOperation_missingPathVariable,
1352                                    sourceResource.getName());
1353                }
1354                return NLS
1355                        .bind(
1356                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_resourceDeleted,
1357                                sourceResource.getName());
1358
1359            }
1360            if (sourceLocation.equals(destinationLocation)) {
1361                return NLS
1362                        .bind(
1363                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sameSourceAndDest,
1364                                sourceResource.getName());
1365            }
1366            // is the source a parent of the destination?
1367
if (new Path(sourceLocation.toString()).isPrefixOf(new Path(
1368                    destinationLocation.toString()))) {
1369                return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationDescendentError;
1370            }
1371
1372            String JavaDoc linkedResourceMessage = validateLinkedResource(destination,
1373                    sourceResource);
1374            if (linkedResourceMessage != null) {
1375                return linkedResourceMessage;
1376            }
1377        }
1378        return null;
1379    }
1380
1381    /**
1382     * Validates that the given source resources can be copied to the
1383     * destination as decided by the VCM provider.
1384     *
1385     * @param destination
1386     * copy destination
1387     * @param sourceResources
1388     * source resources
1389     * @return <code>true</code> all files passed validation or there were no
1390     * files to validate. <code>false</code> one or more files did not
1391     * pass validation.
1392     */

1393    private boolean validateEdit(IContainer destination,
1394            IResource[] sourceResources) {
1395        ArrayList JavaDoc copyFiles = new ArrayList JavaDoc();
1396
1397        collectExistingReadonlyFiles(destination.getFullPath(),
1398                sourceResources, copyFiles);
1399        if (copyFiles.size() > 0) {
1400            IFile[] files = (IFile[]) copyFiles.toArray(new IFile[copyFiles
1401                    .size()]);
1402            IWorkspace workspace = ResourcesPlugin.getWorkspace();
1403            IStatus status = workspace.validateEdit(files, messageShell);
1404
1405            canceled = status.isOK() == false;
1406            return status.isOK();
1407        }
1408        return true;
1409    }
1410
1411    /**
1412     * Checks whether the destination is valid for copying the source files.
1413     * <p>
1414     * Note this method is for internal use only. It is not API.
1415     * </p>
1416     *
1417     * @param destination
1418     * the destination container
1419     * @param sourceNames
1420     * the source file names
1421     * @return an error message, or <code>null</code> if the path is valid
1422     */

1423    public String JavaDoc validateImportDestination(IContainer destination,
1424            String JavaDoc[] sourceNames) {
1425
1426        IFileStore[] stores = new IFileStore[sourceNames.length];
1427        for (int i = 0; i < sourceNames.length; i++) {
1428            IFileStore store = IDEResourceInfoUtils
1429                    .getFileStore(sourceNames[i]);
1430            if (store == null) {
1431                return NLS
1432                        .bind(
1433                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_infoNotFound,
1434                                sourceNames[i]);
1435            }
1436            stores[i] = store;
1437        }
1438        return validateImportDestinationInternal(destination, stores);
1439
1440    }
1441
1442    /**
1443     * Checks whether the destination is valid for copying the source file
1444     * stores.
1445     * <p>
1446     * Note this method is for internal use only. It is not API.
1447     * </p>
1448     * <p>
1449     * TODO Bug 117804. This method has been renamed to avoid a bug in the
1450     * Eclipse compiler with regards to visibility and type resolution when
1451     * linking.
1452     * </p>
1453     *
1454     * @param destination
1455     * the destination container
1456     * @param sourceStores
1457     * the source IFileStore
1458     * @return an error message, or <code>null</code> if the path is valid
1459     */

1460    private String JavaDoc validateImportDestinationInternal(IContainer destination,
1461            IFileStore[] sourceStores) {
1462        if (!isAccessible(destination))
1463            return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationAccessError;
1464
1465        IFileStore destinationStore;
1466        try {
1467            destinationStore = EFS.getStore(destination.getLocationURI());
1468        } catch (CoreException exception) {
1469            IDEWorkbenchPlugin.log(exception.getLocalizedMessage(), exception);
1470            return NLS
1471                    .bind(
1472                            IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
1473                            exception.getLocalizedMessage());
1474        }
1475        for (int i = 0; i < sourceStores.length; i++) {
1476            IFileStore sourceStore = sourceStores[i];
1477            IFileStore sourceParentStore = sourceStore.getParent();
1478
1479            if (sourceStore != null) {
1480                if (destinationStore.equals(sourceStore)
1481                        || (sourceParentStore != null && destinationStore
1482                                .equals(sourceParentStore))) {
1483                    return NLS
1484                            .bind(
1485                                    IDEWorkbenchMessages.CopyFilesAndFoldersOperation_importSameSourceAndDest,
1486                                    sourceStore.getName());
1487                }
1488                // work around bug 16202. replacement for
1489
// sourcePath.isPrefixOf(destinationPath)
1490
IFileStore destinationParent = destinationStore.getParent();
1491                if (sourceStore.isParentOf(destinationParent)) {
1492                    return IDEWorkbenchMessages.CopyFilesAndFoldersOperation_destinationDescendentError;
1493                }
1494
1495            }
1496        }
1497        return null;
1498    }
1499
1500    /**
1501     * Check if the destination is valid for the given source resource.
1502     *
1503     * @param destination
1504     * destination container of the operation
1505     * @param source
1506     * source resource
1507     * @return String error message or null if the destination is valid
1508     */

1509    private String JavaDoc validateLinkedResource(IContainer destination,
1510            IResource source) {
1511        if (source.isLinked() == false) {
1512            return null;
1513        }
1514        IWorkspace workspace = destination.getWorkspace();
1515        IResource linkHandle = createLinkedResourceHandle(destination, source);
1516        IStatus locationStatus = workspace.validateLinkLocation(linkHandle,
1517                source.getRawLocation());
1518
1519        if (locationStatus.getSeverity() == IStatus.ERROR) {
1520            return locationStatus.getMessage();
1521        }
1522        IPath sourceLocation = source.getLocation();
1523        if (source.getProject().equals(destination.getProject()) == false
1524                && source.getType() == IResource.FOLDER
1525                && sourceLocation != null) {
1526            // prevent merging linked folders that point to the same
1527
// file system folder
1528
try {
1529                IResource[] members = destination.members();
1530                for (int j = 0; j < members.length; j++) {
1531                    if (sourceLocation.equals(members[j].getLocation())
1532                            && source.getName().equals(members[j].getName())) {
1533                        return NLS
1534                                .bind(
1535                                        IDEWorkbenchMessages.CopyFilesAndFoldersOperation_sameSourceAndDest,
1536                                        source.getName());
1537                    }
1538                }
1539            } catch (CoreException exception) {
1540                displayError(NLS
1541                        .bind(
1542                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_internalError,
1543                                exception.getMessage()));
1544            }
1545        }
1546        return null;
1547    }
1548
1549    /**
1550     * Returns whether moving all of the given source resources to the given
1551     * destination container could be done without causing name collisions.
1552     *
1553     * @param destination
1554     * the destination container
1555     * @param sourceResources
1556     * the list of resources
1557     * @return <code>true</code> if there would be no name collisions, and
1558     * <code>false</code> if there would
1559     */

1560    private IResource[] validateNoNameCollisions(IContainer destination,
1561            IResource[] sourceResources) {
1562        List JavaDoc copyItems = new ArrayList JavaDoc();
1563        IWorkspaceRoot workspaceRoot = destination.getWorkspace().getRoot();
1564        int overwrite = IDialogConstants.NO_ID;
1565
1566        // Check to see if we would be overwriting a parent folder.
1567
// Cancel entire copy operation if we do.
1568
for (int i = 0; i < sourceResources.length; i++) {
1569            final IResource sourceResource = sourceResources[i];
1570            final IPath destinationPath = destination.getFullPath().append(
1571                    sourceResource.getName());
1572            final IPath sourcePath = sourceResource.getFullPath();
1573
1574            IResource newResource = workspaceRoot.findMember(destinationPath);
1575            if (newResource != null && destinationPath.isPrefixOf(sourcePath)) {
1576                displayError(NLS
1577                        .bind(
1578                                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_overwriteProblem,
1579                                destinationPath, sourcePath));
1580
1581                canceled = true;
1582                return null;
1583            }
1584        }
1585        // Check for overwrite conflicts
1586
for (int i = 0; i < sourceResources.length; i++) {
1587            final IResource source = sourceResources[i];
1588            final IPath destinationPath = destination.getFullPath().append(
1589                    source.getName());
1590
1591            IResource newResource = workspaceRoot.findMember(destinationPath);
1592            if (newResource != null) {
1593                if (overwrite != IDialogConstants.YES_TO_ALL_ID
1594                        || (newResource.getType() == IResource.FOLDER && homogenousResources(
1595                                source, destination) == false)) {
1596                    overwrite = checkOverwrite(source, newResource);
1597                }
1598                if (overwrite == IDialogConstants.YES_ID
1599                        || overwrite == IDialogConstants.YES_TO_ALL_ID) {
1600                    copyItems.add(source);
1601                } else if (overwrite == IDialogConstants.CANCEL_ID) {
1602                    canceled = true;
1603                    return null;
1604                }
1605            } else {
1606                copyItems.add(source);
1607            }
1608        }
1609        return (IResource[]) copyItems.toArray(new IResource[copyItems.size()]);
1610    }
1611
1612    private void copyResources(final IResource[] resources,
1613            final IPath destinationPath, final IResource[][] copiedResources,
1614            IProgressMonitor monitor) {
1615        IResource[] copyResources = resources;
1616
1617        // Fix for bug 31116. Do not provide a task name when
1618
// creating the task.
1619
monitor.beginTask("", 100); //$NON-NLS-1$
1620
monitor.setTaskName(getOperationTitle());
1621        monitor.worked(10); // show some initial progress
1622

1623        // Checks only required if this is an exisiting container path.
1624
boolean copyWithAutoRename = false;
1625        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
1626        if (root.exists(destinationPath)) {
1627            IContainer container = (IContainer) root
1628                    .findMember(destinationPath);
1629            // If we're copying to the source container then perform
1630
// auto-renames on all resources to avoid name collisions.
1631
if (isDestinationSameAsSource(copyResources, container)
1632                    && canPerformAutoRename()) {
1633                copyWithAutoRename = true;
1634            } else {
1635                // If no auto-renaming will be happening, check for
1636
// potential name collisions at the target resource
1637
copyResources = validateNoNameCollisions(container,
1638                        copyResources);
1639                if (copyResources == null) {
1640                    if (canceled) {
1641                        return;
1642                    }
1643                    displayError(IDEWorkbenchMessages.CopyFilesAndFoldersOperation_nameCollision);
1644                    return;
1645                }
1646                if (validateEdit(container, copyResources) == false) {
1647                    return;
1648                }
1649            }
1650        }
1651
1652        errorStatus = null;
1653        if (copyResources.length > 0) {
1654            if (copyWithAutoRename) {
1655                performCopyWithAutoRename(copyResources, destinationPath,
1656                        new SubProgressMonitor(monitor, 90));
1657            } else {
1658                performCopy(copyResources, destinationPath, new SubProgressMonitor(monitor, 90));
1659            }
1660        }
1661        monitor.done();
1662        copiedResources[0] = copyResources;
1663    }
1664
1665    private void copyFileStores(final IFileStore[] stores,
1666            final IPath destinationPath, IProgressMonitor monitor) {
1667        // Checks only required if this is an exisiting container path.
1668
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
1669        if (root.exists(destinationPath)) {
1670            IContainer container = (IContainer) root
1671                    .findMember(destinationPath);
1672
1673            performFileImport(stores, container, monitor);
1674        }
1675    }
1676
1677    /**
1678     * Returns the model provider ids that are known to the client that
1679     * instantiated this operation.
1680     *
1681     * @return the model provider ids that are known to the client that
1682     * instantiated this operation.
1683     * @since 3.2
1684     */

1685    public String JavaDoc[] getModelProviderIds() {
1686        return modelProviderIds;
1687    }
1688
1689    /**
1690     * Sets the model provider ids that are known to the client that
1691     * instantiated this operation. Any potential side effects reported by these
1692     * models during validation will be ignored.
1693     *
1694     * @param modelProviderIds
1695     * the model providers known to the client who is using this
1696     * operation.
1697     * @since 3.2
1698     */

1699    public void setModelProviderIds(String JavaDoc[] modelProviderIds) {
1700        this.modelProviderIds = modelProviderIds;
1701    }
1702
1703    /**
1704     * Returns an AbstractWorkspaceOperation suitable for performing the move or
1705     * copy operation that will move or copy the given resources to the given
1706     * destination path.
1707     *
1708     * @param resources
1709     * the resources to be moved or copied
1710     * @param destinationPath
1711     * the destination path to which the resources should be moved
1712     * @return the operation that should be used to perform the move or cop
1713     * @since 3.3
1714     */

1715    protected AbstractWorkspaceOperation getUndoableCopyOrMoveOperation(
1716            IResource[] resources, IPath destinationPath) {
1717        return new CopyResourcesOperation(resources, destinationPath,
1718                IDEWorkbenchMessages.CopyFilesAndFoldersOperation_copyTitle);
1719
1720    }
1721}
1722
Popular Tags