| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.wizards.datatransfer; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.operation.ModalContext; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.dialogs.IOverwriteQuery; |
| |
| |
| /** |
| * Operation for exporting the contents of a resource to the local file system. |
| */ |
| public class FileSystemExportOperation implements IRunnableWithProgress { |
| private IPath path; |
| |
| private IProgressMonitor monitor; |
| |
| private FileSystemExporter exporter = new FileSystemExporter(); |
| |
| private List resourcesToExport; |
| |
| private IOverwriteQuery overwriteCallback; |
| |
| private IResource resource; |
| |
| private List errorTable = new ArrayList(1); |
| |
| //The constants for the overwrite 3 state |
| private static final int OVERWRITE_NOT_SET = 0; |
| |
| private static final int OVERWRITE_NONE = 1; |
| |
| private static final int OVERWRITE_ALL = 2; |
| |
| private int overwriteState = OVERWRITE_NOT_SET; |
| |
| private boolean createLeadupStructure = true; |
| |
| private boolean createContainerDirectories = true; |
| |
| private boolean resolveLinks = false; |
| |
| /** |
| * Create an instance of this class. Use this constructor if you wish to |
| * recursively export a single resource |
| */ |
| public FileSystemExportOperation(IResource res, String destinationPath, |
| IOverwriteQuery overwriteImplementor) { |
| super(); |
| resource = res; |
| path = new Path(destinationPath); |
| overwriteCallback = overwriteImplementor; |
| } |
| |
| /** |
| * Create an instance of this class. Use this constructor if you wish to |
| * export specific resources with a common parent resource (affects container |
| * directory creation) |
| */ |
| public FileSystemExportOperation(IResource res, List resources, |
| String destinationPath, IOverwriteQuery overwriteImplementor) { |
| this(res, destinationPath, overwriteImplementor); |
| resourcesToExport = resources; |
| } |
| |
| /** |
| * Answer the total number of file resources that exist at or below self in the |
| * resources hierarchy. |
| * |
| * @return int |
| * @param parentResource org.eclipse.core.resources.IResource |
| */ |
| protected int countChildrenOf(IResource parentResource) |
| throws CoreException { |
| if (parentResource.getType() == IResource.FILE) { |
| return 1; |
| } |
| |
| int count = 0; |
| if (parentResource.isAccessible()) { |
| for (IResource child : ((IContainer) parentResource).members()) { |
| count += countChildrenOf(child); |
| } |
| } |
| |
| return count; |
| } |
| |
| /** |
| * Answer a boolean indicating the number of file resources that were |
| * specified for export |
| * |
| * @return int |
| */ |
| protected int countSelectedResources() throws CoreException { |
| int result = 0; |
| Iterator resources = resourcesToExport.iterator(); |
| |
| while (resources.hasNext()) { |
| result += countChildrenOf((IResource) resources.next()); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Create the directories required for exporting the passed resource, |
| * based upon its container hierarchy |
| * |
| * @param childResource org.eclipse.core.resources.IResource |
| */ |
| protected void createLeadupDirectoriesFor(IResource childResource) { |
| IPath resourcePath = childResource.getFullPath().removeLastSegments(1); |
| |
| for (int i = 0; i < resourcePath.segmentCount(); i++) { |
| path = path.append(resourcePath.segment(i)); |
| exporter.createFolder(path); |
| } |
| } |
| |
| /** |
| * Recursively export the previously-specified resource |
| */ |
| protected void exportAllResources() throws InterruptedException { |
| if (resource.getType() == IResource.FILE) { |
| exportFile((IFile) resource, path); |
| } else { |
| try { |
| exportChildren(((IContainer) resource).members(), path); |
| } catch (CoreException e) { |
| // not safe to show a dialog |
| // should never happen because the file system export wizard ensures that the |
| // single resource chosen for export is both existent and accessible |
| errorTable.add(e.getStatus()); |
| } |
| } |
| } |
| |
| /** |
| * Export all of the resources contained in the passed collection |
| * |
| * @param children java.util.Enumeration |
| * @param currentPath IPath |
| */ |
| protected void exportChildren(IResource[] children, IPath currentPath) |
| throws InterruptedException { |
| for (IResource child : children) { |
| if (!child.isAccessible() || (!resolveLinks && child.isLinked())) { |
| continue; |
| } |
| |
| if (child.getType() == IResource.FILE) { |
| exportFile((IFile) child, currentPath); |
| } else { |
| IPath destination = currentPath.append(child.getName()); |
| exporter.createFolder(destination); |
| try { |
| exportChildren(((IContainer) child).members(), destination); |
| } catch (CoreException e) { |
| // not safe to show a dialog |
| // should never happen because: |
| // i. this method is called recursively iterating over the result of #members, |
| // which only answers existing children |
| // ii. there is an #isAccessible check done before #members is invoked |
| errorTable.add(e.getStatus()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Export the passed file to the specified location |
| * |
| * @param file org.eclipse.core.resources.IFile |
| * @param location org.eclipse.core.runtime.IPath |
| */ |
| protected void exportFile(IFile file, IPath location) |
| throws InterruptedException { |
| IPath fullPath = location.append(file.getName()); |
| monitor.subTask(file.getFullPath().toString()); |
| String properPathString = fullPath.toOSString(); |
| File targetFile = new File(properPathString); |
| |
| if (targetFile.exists()) { |
| if (!targetFile.canWrite()) { |
| errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, |
| 0, NLS.bind(DataTransferMessages.DataTransfer_cannotOverwrite, targetFile.getAbsolutePath()), |
| null)); |
| monitor.worked(1); |
| return; |
| } |
| |
| if (overwriteState == OVERWRITE_NONE) { |
| return; |
| } |
| |
| if (overwriteState != OVERWRITE_ALL) { |
| String overwriteAnswer = overwriteCallback |
| .queryOverwrite(properPathString); |
| |
| if (overwriteAnswer.equals(IOverwriteQuery.CANCEL)) { |
| throw new InterruptedException(); |
| } |
| |
| if (overwriteAnswer.equals(IOverwriteQuery.NO)) { |
| monitor.worked(1); |
| return; |
| } |
| |
| if (overwriteAnswer.equals(IOverwriteQuery.NO_ALL)) { |
| monitor.worked(1); |
| overwriteState = OVERWRITE_NONE; |
| return; |
| } |
| |
| if (overwriteAnswer.equals(IOverwriteQuery.ALL)) { |
| overwriteState = OVERWRITE_ALL; |
| } |
| } |
| } |
| |
| try { |
| exporter.write(file, fullPath); |
| } catch (IOException e) { |
| errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, |
| NLS.bind(DataTransferMessages.DataTransfer_errorExporting, fullPath, e.getMessage()), e)); |
| } catch (CoreException e) { |
| errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, |
| NLS.bind(DataTransferMessages.DataTransfer_errorExporting, fullPath, e.getMessage()), e)); |
| } |
| |
| monitor.worked(1); |
| ModalContext.checkCanceled(monitor); |
| } |
| |
| /** |
| * Export the resources contained in the previously-defined |
| * resourcesToExport collection |
| */ |
| protected void exportSpecifiedResources() throws InterruptedException { |
| Iterator resources = resourcesToExport.iterator(); |
| IPath initPath = (IPath) path.clone(); |
| |
| while (resources.hasNext()) { |
| IResource currentResource = (IResource) resources.next(); |
| if (!currentResource.isAccessible() || (!resolveLinks && currentResource.isLinked())) { |
| continue; |
| } |
| |
| path = initPath; |
| |
| if (resource == null) { |
| // No root resource specified and creation of containment directories |
| // is required. Create containers from depth 2 onwards (ie.- project's |
| // child inclusive) for each resource being exported. |
| if (createLeadupStructure) { |
| createLeadupDirectoriesFor(currentResource); |
| } |
| |
| } else { |
| // Root resource specified. Must create containment directories |
| // from this point onwards for each resource being exported |
| IPath containersToCreate = currentResource.getFullPath() |
| .removeFirstSegments( |
| resource.getFullPath().segmentCount()) |
| .removeLastSegments(1); |
| |
| for (int i = 0; i < containersToCreate.segmentCount(); i++) { |
| path = path.append(containersToCreate.segment(i)); |
| exporter.createFolder(path); |
| } |
| } |
| |
| if (currentResource.getType() == IResource.FILE) { |
| exportFile((IFile) currentResource, path); |
| } else { |
| if (createContainerDirectories) { |
| path = path.append(currentResource.getName()); |
| exporter.createFolder(path); |
| } |
| |
| try { |
| exportChildren(((IContainer) currentResource).members(), |
| path); |
| } catch (CoreException e) { |
| // should never happen because #isAccessible is called before #members is invoked, |
| // which implicitly does an existence check |
| errorTable.add(e.getStatus()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the status of the export operation. |
| * If there were any errors, the result is a status object containing |
| * individual status objects for each error. |
| * If there were no errors, the result is a status object with error code <code>OK</code>. |
| * |
| * @return the status |
| */ |
| public IStatus getStatus() { |
| IStatus[] errors = new IStatus[errorTable.size()]; |
| errorTable.toArray(errors); |
| return new MultiStatus( |
| PlatformUI.PLUGIN_ID, |
| IStatus.OK, |
| errors, |
| DataTransferMessages.FileSystemExportOperation_problemsExporting, |
| null); |
| } |
| |
| /** |
| * Answer a boolean indicating whether the passed child is a descendent |
| * of one or more members of the passed resources collection |
| * |
| * @return boolean |
| * @param resources java.util.List |
| * @param child org.eclipse.core.resources.IResource |
| */ |
| protected boolean isDescendent(List resources, IResource child) { |
| if (child.getType() == IResource.PROJECT) { |
| return false; |
| } |
| |
| IResource parent = child.getParent(); |
| if (resources.contains(parent)) { |
| return true; |
| } |
| |
| return isDescendent(resources, parent); |
| } |
| |
| /** |
| * Export the resources that were previously specified for export |
| * (or if a single resource was specified then export it recursively) |
| */ |
| @Override |
| public void run(IProgressMonitor progressMonitor) |
| throws InterruptedException { |
| this.monitor = progressMonitor; |
| |
| if (resource != null) { |
| if (createLeadupStructure) { |
| createLeadupDirectoriesFor(resource); |
| } |
| |
| if (createContainerDirectories |
| && resource.getType() != IResource.FILE) { |
| // ensure it's a container |
| path = path.append(resource.getName()); |
| exporter.createFolder(path); |
| } |
| } |
| |
| try { |
| int totalWork = IProgressMonitor.UNKNOWN; |
| try { |
| if (resourcesToExport == null) { |
| totalWork = countChildrenOf(resource); |
| } else { |
| totalWork = countSelectedResources(); |
| } |
| } catch (CoreException e) { |
| // Should not happen |
| errorTable.add(e.getStatus()); |
| } |
| monitor.beginTask(DataTransferMessages.DataTransfer_exportingTitle, totalWork); |
| if (resourcesToExport == null) { |
| exportAllResources(); |
| } else { |
| exportSpecifiedResources(); |
| } |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| /** |
| * Set this boolean indicating whether a directory should be created for |
| * Folder resources that are explicitly passed for export |
| * |
| * @param value boolean |
| */ |
| public void setCreateContainerDirectories(boolean value) { |
| createContainerDirectories = value; |
| } |
| |
| /** |
| * Set this boolean indicating whether each exported resource's complete path should |
| * include containment hierarchies as dictated by its parents |
| * |
| * @param value boolean |
| */ |
| public void setCreateLeadupStructure(boolean value) { |
| createLeadupStructure = value; |
| } |
| |
| /** |
| * Set this boolean indicating whether exported resources should automatically |
| * overwrite existing files when a conflict occurs. If not |
| * query the user. |
| * |
| * @param value boolean |
| */ |
| public void setOverwriteFiles(boolean value) { |
| if (value) { |
| overwriteState = OVERWRITE_ALL; |
| } |
| } |
| |
| /** |
| * Set this boolean indicating whether linked resources should be resolved |
| * and exported (as opposed to simply ignored) |
| * |
| * @param value |
| * boolean |
| */ |
| public void setResolveLinks(boolean value) { |
| resolveLinks = value; |
| } |
| } |