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