/******************************************************************************* * Copyright (c) 2000, 2011 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 * Red Hat, Inc - added bzip2 format support *******************************************************************************/ package org.eclipse.ui.internal.wizards.datatransfer; import java.io.IOException; import java.lang.reflect.InvocationTargetException; 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.Status; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.operation.ModalContext; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; /** * Operation for exporting a resource and its children to a new .zip or * .tar.gz file. * * @since 3.1 */ public class ArchiveFileExportOperation implements IRunnableWithProgress { private IFileExporter exporter; private String destinationFilename; private IProgressMonitor monitor; private List resourcesToExport; private IResource resource; private List errorTable = new ArrayList(1); //IStatus private boolean useCompression = true; private boolean useTarFormat = false; private boolean createLeadupStructure = true; private int tarMode = -1;//Initialize to -1 signifies that it should behave as if before modified (version 1.14) /** * Create an instance of this class. Use this constructor if you wish to * export specific resources without a common parent resource * * @param resources java.util.Vector * @param filename java.lang.String */ public ArchiveFileExportOperation(List resources, String filename) { 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(); //Removes currentResource; } } resourcesToExport = resources; destinationFilename = filename; } /** * Create an instance of this class. Use this constructor if you wish * to recursively export a single resource. * * @param res org.eclipse.core.resources.IResource; * @param filename java.lang.String */ public ArchiveFileExportOperation(IResource res, String filename) { super(); resource = res; destinationFilename = filename; } /** * 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) * * @param res org.eclipse.core.resources.IResource * @param resources java.util.Vector * @param filename java.lang.String */ public ArchiveFileExportOperation(IResource res, List resources, String filename) { this(res, filename); resourcesToExport = resources; } /** * 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) and wish to use bzip2 compression method. * * @param res * @param resources * @param filename * @param compressMode * @throws IllegalArgumentException */ public ArchiveFileExportOperation(IResource res, List resources, String filename, int compressMode) throws IllegalArgumentException { this(res, resources, filename); if ((compressMode < TarFileExporter.UNCOMPRESSED) || (compressMode > TarFileExporter.XZ)) throw new IllegalArgumentException(DataTransferMessages.ArchiveFileExportOperation_unsupportedTarMode); tarMode = compressMode; } /** * 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, IDEWorkbenchPlugin.IDE_WORKBENCH, 0, message, e)); } /** * Answer the total number of file resources that exist at or below self * in the resources hierarchy. * * @return int * @param checkResource org.eclipse.core.resources.IResource */ protected int countChildrenOf(IResource checkResource) throws CoreException { if (checkResource.getType() == IResource.FILE) { return 1; } int count = 0; if (checkResource.isAccessible()) { IResource[] children = ((IContainer) checkResource).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; } /** * Export the passed resource to the destination .zip. Export with * no path leadup * * @param exportResource org.eclipse.core.resources.IResource */ protected void exportResource(IResource exportResource) throws InterruptedException { exportResource(exportResource, 1); } /** * Creates and returns the string that should be used as the name of the entry in the archive. * @param exportResource the resource to export * @param leadupDepth the number of resource levels to be included in the path including the resourse itself. */ private String createDestinationName(int leadupDepth, IResource exportResource) { IPath fullPath = exportResource.getFullPath(); if (createLeadupStructure) { return fullPath.makeRelative().toString(); } return fullPath.removeFirstSegments( fullPath.segmentCount() - leadupDepth).toString(); } /** * Export the passed resource to the destination .zip * * @param exportResource org.eclipse.core.resources.IResource * @param leadupDepth the number of resource levels to be included in * the path including the resourse itself. */ protected void exportResource(IResource exportResource, int leadupDepth) throws InterruptedException { if (!exportResource.isAccessible()) { return; } if (exportResource.getType() == IResource.FILE) { String destinationName = createDestinationName(leadupDepth, exportResource); monitor.subTask(destinationName); try { exporter.write((IFile) exportResource, destinationName); } catch (IOException e) { addError(NLS.bind(DataTransferMessages.DataTransfer_errorExporting, exportResource.getFullPath().makeRelative(), e.getMessage()), e); } catch (CoreException e) { addError(NLS.bind(DataTransferMessages.DataTransfer_errorExporting, exportResource.getFullPath().makeRelative(), e.getMessage()), e); } monitor.worked(1); ModalContext.checkCanceled(monitor); } else { IResource[] children = null; try { children = ((IContainer) exportResource).members(); } catch (CoreException e) { // this should never happen because an #isAccessible check is done before #members is invoked addError(NLS.bind(DataTransferMessages.DataTransfer_errorExporting, exportResource.getFullPath()), e); } if (children.length == 0) { // create an entry for empty containers, see bug 278402 String destinationName = createDestinationName(leadupDepth, exportResource); try { exporter.write((IContainer) exportResource, destinationName + IPath.SEPARATOR); } catch (IOException e) { addError(NLS.bind(DataTransferMessages.DataTransfer_errorExporting, exportResource.getFullPath().makeRelative(), e.getMessage()), e); } } for (int i = 0; i < children.length; i++) { exportResource(children[i], leadupDepth + 1); } } } /** * Export the resources contained in the previously-defined * resourcesToExport collection */ protected void exportSpecifiedResources() throws InterruptedException { Iterator resources = resourcesToExport.iterator(); while (resources.hasNext()) { IResource currentResource = (IResource) resources.next(); exportResource(currentResource); } } /** * Returns the status of the 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( IDEWorkbenchPlugin.IDE_WORKBENCH, IStatus.OK, errors, DataTransferMessages.FileSystemExportOperation_problemsExporting, null); } /** * Initialize this operation, if tarMode is not -1, use the new constructor of TarFileExporter to use bzip2 format, otherwise, * the old TarFileExporter constructor is called. * * @exception java.io.IOException */ protected void initialize() throws IOException { if (useTarFormat) { if (tarMode != -1) { try { exporter = new TarFileExporter(destinationFilename, tarMode); } catch (IllegalArgumentException iae) { // Should not happen because it is caught by the // constructor. } } else exporter = new TarFileExporter(destinationFilename, useCompression); } else { exporter = new ZipFileExporter(destinationFilename, useCompression); } } /** * 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.Vector * @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 InvocationTargetException, InterruptedException { this.monitor = progressMonitor; try { initialize(); } catch (IOException e) { throw new InvocationTargetException(e, NLS.bind(DataTransferMessages.ZipExport_cannotOpen, e.getMessage())); } try { // ie.- a single resource for recursive export was specified int totalWork = IProgressMonitor.UNKNOWN; try { if (resourcesToExport == null) { totalWork = countChildrenOf(resource); } else { totalWork = countSelectedResources(); } } catch (CoreException e) { // Should not happen } monitor.beginTask(DataTransferMessages.DataTransfer_exportingTitle, totalWork); if (resourcesToExport == null) { exportResource(resource); } else { // ie.- a list of specific resources to export was specified exportSpecifiedResources(); } try { exporter.finished(); } catch (IOException e) { throw new InvocationTargetException( e, NLS.bind(DataTransferMessages.ZipExport_cannotClose, e.getMessage())); } } finally { monitor.done(); } } /** * Set this boolean indicating whether each exported resource's 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 * be compressed (as opposed to simply being stored) * * @param value boolean */ public void setUseCompression(boolean value) { useCompression = value; } /** * Set this boolean indicating whether the file should be output * in tar.gz format rather than .zip format. * * @param value boolean */ public void setUseTarFormat(boolean value) { useTarFormat = value; } }