blob: 0e8cc39eee6dd517c0b80047023c826ad55d1222 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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;
/**
* Create an instance of this class. Use this constructor if you wish to
* export specific resources without a common parent resource
*/
public FileSystemExportOperation(List resources, String destinationPath,
IOverwriteQuery overwriteImplementor) {
super();
// Eliminate redundancies in list of resources being exported
Iterator elementsEnum = resources.iterator();
while (elementsEnum.hasNext()) {
IResource currentResource = (IResource) elementsEnum.next();
if (isDescendent(resources, currentResource))
elementsEnum.remove(); //Remove currentResource
}
resourcesToExport = resources;
path = new Path(destinationPath);
overwriteCallback = overwriteImplementor;
}
/**
* 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;
}
/**
* Add a new entry to the error table with the passed information
*/
protected void addError(String message, Throwable e) {
errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0,
message, e));
}
/**
* 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()) {
IResource[] children = ((IContainer) parentResource).members();
for (int i = 0; i < children.length; i++)
count += countChildrenOf(children[i]);
}
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);
}
}
}
/**
* 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 (int i = 0; i < children.length; i++) {
IResource child = children[i];
if (!child.isAccessible())
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())
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)
*/
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;
}
}