From 6120dc638d8ea1aaca8dee3b4a0244d582c4b241 Mon Sep 17 00:00:00 2001 From: Marc-Andre Laperle Date: Wed, 5 Oct 2016 18:22:43 -0400 Subject: [PATCH] tmf: Use Apache Common Compress for importing from archive By using Apache Common Compress instead of the Eclipse classes, it will allow us to support more formats and have move stability. We will be able to handle tars with PaxHeaders, XZ and BZip compression for example. Change-Id: I2693da15eb38a57b477734f1a0931c30585f2192 Signed-off-by: Marc-Andre Laperle Signed-off-by: Patrick Tasse Reviewed-on: https://git.eclipse.org/r/82567 Reviewed-by: Hudson CI --- rcp/org.eclipse.tracecompass.rcp/feature.xml | 7 + rcp/org.eclipse.tracecompass.rcp/pom.xml | 1 + .../META-INF/MANIFEST.MF | 3 +- .../wizards/importtrace/ArchiveUtil.java | 51 ++- ...leSystemObjectImportStructureProvider.java | 32 +- .../importtrace/ImportTraceWizardPage.java | 2 +- .../SafePathZipLeveledStructureProvider.java | 74 ---- .../project/wizards/importtrace/TarEntry.java | 142 -------- .../wizards/importtrace/TarException.java | 49 --- .../project/wizards/importtrace/TarFile.java | 270 +++++++------- .../importtrace/TarFileSystemObject.java | 7 +- .../wizards/importtrace/TarInputStream.java | 343 ------------------ .../TarLeveledStructureProvider.java | 62 ++-- .../TraceValidateAndImportOperation.java | 50 +++ .../importtrace/ZipFileSystemObject.java | 6 +- .../ZipLeveledStructureProvider.java | 252 +++++++++++++ .../AbstractTracePackageOperation.java | 72 ++-- .../TracePackageImportOperation.java | 5 +- 18 files changed, 575 insertions(+), 853 deletions(-) delete mode 100644 tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/SafePathZipLeveledStructureProvider.java delete mode 100644 tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarEntry.java delete mode 100644 tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarException.java delete mode 100644 tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarInputStream.java create mode 100644 tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ZipLeveledStructureProvider.java diff --git a/rcp/org.eclipse.tracecompass.rcp/feature.xml b/rcp/org.eclipse.tracecompass.rcp/feature.xml index 00b43c4a2f..2a048ef6f5 100644 --- a/rcp/org.eclipse.tracecompass.rcp/feature.xml +++ b/rcp/org.eclipse.tracecompass.rcp/feature.xml @@ -274,4 +274,11 @@ version="0.0.0" unpack="false"/> + + diff --git a/rcp/org.eclipse.tracecompass.rcp/pom.xml b/rcp/org.eclipse.tracecompass.rcp/pom.xml index fcd527c796..2f4f3f821a 100644 --- a/rcp/org.eclipse.tracecompass.rcp/pom.xml +++ b/rcp/org.eclipse.tracecompass.rcp/pom.xml @@ -36,6 +36,7 @@ + diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/META-INF/MANIFEST.MF b/tmf/org.eclipse.tracecompass.tmf.ui/META-INF/MANIFEST.MF index 6c964c0b44..0b46129d75 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/META-INF/MANIFEST.MF +++ b/tmf/org.eclipse.tracecompass.tmf.ui/META-INF/MANIFEST.MF @@ -24,7 +24,8 @@ Require-Bundle: org.eclipse.core.expressions, org.swtchart, com.ibm.icu, org.eclipse.linuxtools.dataviewers.piechart, - org.eclipse.tracecompass.segmentstore.core + org.eclipse.tracecompass.segmentstore.core, + org.apache.commons.compress Export-Package: org.eclipse.tracecompass.internal.tmf.ui;x-friends:="org.eclipse.tracecompass.tmf.ui.tests,org.eclipse.tracecompass.tmf.ctf.ui.tests", org.eclipse.tracecompass.internal.tmf.ui.commands;x-internal:=true, org.eclipse.tracecompass.internal.tmf.ui.dialogs;x-internal:=true, diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ArchiveUtil.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ArchiveUtil.java index 559dad9e96..e1ff0fa611 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ArchiveUtil.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ArchiveUtil.java @@ -14,22 +14,19 @@ package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; import java.io.File; import java.io.IOException; -import java.util.zip.ZipException; -import java.util.zip.ZipFile; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.swt.widgets.Shell; import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceCoreUtils; import org.eclipse.tracecompass.tmf.core.util.Pair; -import org.eclipse.ui.internal.wizards.datatransfer.ArchiveFileManipulations; import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider; /** * Various utilities for dealing with archives in the context of importing * traces. */ -@SuppressWarnings({"restriction" }) public class ArchiveUtil { /** @@ -42,7 +39,21 @@ public class ArchiveUtil { */ public static boolean isArchiveFile(File sourceFile) { String absolutePath = sourceFile.getAbsolutePath(); - return isTarFile(absolutePath) || ArchiveFileManipulations.isZipFile(absolutePath) || isGzipFile(absolutePath); + return isTarFile(absolutePath) || isZipFile(absolutePath) || isGzipFile(absolutePath); + } + + private static boolean isZipFile(String fileName) { + ZipFile specifiedZipSourceFile = getSpecifiedZipSourceFile(fileName); + if (specifiedZipSourceFile != null) { + try { + specifiedZipSourceFile.close(); + return true; + } catch (IOException e) { + // ignore + } + } + + return false; } private static boolean isTarFile(String fileName) { @@ -73,10 +84,13 @@ public class ArchiveUtil { return null; } + File file = new File(fileName); + if (file.isDirectory()) { + return null; + } + try { - return new ZipFile(fileName); - } catch (ZipException e) { - // ignore + return new ZipFile(file); } catch (IOException e) { // ignore } @@ -96,20 +110,29 @@ public class ArchiveUtil { try { return new TarFile(fileName); - } catch (TarException | IOException e) { + } catch (IOException e) { // ignore } return null; } - @SuppressWarnings("resource") - static boolean ensureZipSourceIsValid(String archivePath, Shell shell) { + static boolean ensureZipSourceIsValid(String archivePath) { ZipFile specifiedFile = getSpecifiedZipSourceFile(archivePath); if (specifiedFile == null) { return false; } - return ArchiveFileManipulations.closeZipFile(specifiedFile, shell); + return closeZipFile(specifiedFile); + } + + static boolean closeZipFile(ZipFile file) { + try { + file.close(); + } catch (IOException e) { + return false; + } + + return true; } static boolean ensureTarSourceIsValid(String archivePath) { @@ -169,11 +192,11 @@ public class ArchiveUtil { TarFile tarFile = getSpecifiedTarSourceFile(archivePath); leveledImportStructureProvider = new FileSystemObjectLeveledImportStructureProvider(new TarLeveledStructureProvider(tarFile), archivePath); } - } else if (ensureZipSourceIsValid(archivePath, shell)) { + } else if (ensureZipSourceIsValid(archivePath)) { // We close the file when we dispose the import provider, see // disposeSelectionGroupRoot ZipFile zipFile = getSpecifiedZipSourceFile(archivePath); - leveledImportStructureProvider = new FileSystemObjectLeveledImportStructureProvider(new SafePathZipLeveledStructureProvider(zipFile), archivePath); + leveledImportStructureProvider = new FileSystemObjectLeveledImportStructureProvider(new ZipLeveledStructureProvider(zipFile), archivePath); } else if (ensureGzipSourceIsValid(archivePath)) { // We close the file when we dispose the import provider, see // disposeSelectionGroupRoot diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/FileSystemObjectImportStructureProvider.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/FileSystemObjectImportStructureProvider.java index 02ddad60e9..3a33240b40 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/FileSystemObjectImportStructureProvider.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/FileSystemObjectImportStructureProvider.java @@ -15,15 +15,15 @@ package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; import java.io.File; import java.io.InputStream; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; -import java.util.zip.ZipEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider; /** * An import provider that makes use of the IFileSystemObject abstraction - * instead of using plain file system objects (File, TarEntry, ZipEntry, etc) + * instead of using plain file system objects (File, TarArchiveEntry, ZipArchiveEntry, etc) */ public class FileSystemObjectImportStructureProvider implements IImportStructureProvider { @@ -43,23 +43,6 @@ public class FileSystemObjectImportStructureProvider implements IImportStructure fArchivePath = archivePath; } - /** - * This orders by files first then the folders. Then by lexical order. - */ - private final class FileObjectPathComparator implements Comparator { - @Override - public int compare(IFileSystemObject o1, IFileSystemObject o2) { - if (o1.isDirectory() != o2.isDirectory()) { - if (o1.isDirectory()) { - return 1; - } - return -1; - } - - return o1.getName().compareToIgnoreCase(o2.getName()); - } - } - @Override public List getChildren(Object element) { @SuppressWarnings("rawtypes") @@ -69,7 +52,6 @@ public class FileSystemObjectImportStructureProvider implements IImportStructure adapted.add(getIFileSystemObject(o)); } - adapted.sort(new FileObjectPathComparator()); return adapted; } @@ -87,10 +69,10 @@ public class FileSystemObjectImportStructureProvider implements IImportStructure if (o instanceof File) { return new FileFileSystemObject((File) o); - } else if (o instanceof TarEntry) { - return new TarFileSystemObject((TarEntry) o, fArchivePath); - } else if (o instanceof ZipEntry) { - return new ZipFileSystemObject((ZipEntry) o, fArchivePath); + } else if (o instanceof TarArchiveEntry) { + return new TarFileSystemObject((TarArchiveEntry) o, fArchivePath); + } else if (o instanceof ZipArchiveEntry) { + return new ZipFileSystemObject((ZipArchiveEntry) o, fArchivePath); } else if (o instanceof GzipEntry) { return new GzipFileSystemObject((GzipEntry) o, fArchivePath); } diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ImportTraceWizardPage.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ImportTraceWizardPage.java index 9ee72e6339..00278eec88 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ImportTraceWizardPage.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ImportTraceWizardPage.java @@ -943,7 +943,7 @@ public class ImportTraceWizardPage extends WizardResourceImportPage { return false; } - if (!isImportFromDirectory() && !ArchiveUtil.ensureTarSourceIsValid(source.getAbsolutePath()) && !ArchiveUtil.ensureZipSourceIsValid(source.getAbsolutePath(), getContainer().getShell()) + if (!isImportFromDirectory() && !ArchiveUtil.ensureTarSourceIsValid(source.getAbsolutePath()) && !ArchiveUtil.ensureZipSourceIsValid(source.getAbsolutePath()) && !ArchiveUtil.ensureGzipSourceIsValid(source.getAbsolutePath())) { setMessage(null); setErrorMessage(Messages.ImportTraceWizard_BadArchiveFormat); diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/SafePathZipLeveledStructureProvider.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/SafePathZipLeveledStructureProvider.java deleted file mode 100644 index fcb6fe635d..0000000000 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/SafePathZipLeveledStructureProvider.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Ericsson - * - * 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 - *******************************************************************************/ - -package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; - -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import org.eclipse.ui.internal.wizards.datatransfer.ZipLeveledStructureProvider; - -/** - * A Zip structure provider that makes sure to return safe paths. For example, - * if a Zip entry contains a ':' and is extracted on Windows, it will be changed - * to a '_' - */ -@SuppressWarnings("restriction") -public class SafePathZipLeveledStructureProvider extends ZipLeveledStructureProvider { - - /** - * Creates a provider which will operate on the passed Zip file. - * - * @param sourceFile - * The source file to create the provider around - */ - public SafePathZipLeveledStructureProvider(ZipFile sourceFile) { - super(sourceFile); - } - - @Override - public String getFullPath(Object element) { - String name = ((ZipEntry) element).getName(); - return ArchiveUtil.toValidNamesPath(name).toOSString(); - } - - @Override - public String getLabel(Object element) { - if (element.equals(getRoot())) { - return ((ZipEntry) element).getName(); - } - String name = ((ZipEntry) element).getName(); - return stripPath(ArchiveUtil.toValidNamesPath(name).lastSegment()); - } - - /** - * Strip the leading directories from the path. Copied from - * {@link ZipLeveledStructureProvider} - */ - private String stripPath(String path) { - String strippedPath = path; - String pathOrig = new String(strippedPath); - for (int i = 0; i < getStrip(); i++) { - int firstSep = strippedPath.indexOf('/'); - // If the first character was a separator we must strip to the next - // separator as well - if (firstSep == 0) { - strippedPath = strippedPath.substring(1); - firstSep = strippedPath.indexOf('/'); - } - // No separator was present so we're in a higher directory right - // now - if (firstSep == -1) { - return pathOrig; - } - strippedPath = strippedPath.substring(firstSep); - } - return strippedPath; - } -} diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarEntry.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarEntry.java deleted file mode 100644 index 7fb5a58f6a..0000000000 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarEntry.java +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004, 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 - * Marc-Andre Laperle - Copied to Trace Compass to work around bug 501379 - *******************************************************************************/ -package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; - -/** - * Representation of a file in a tar archive. - */ -public class TarEntry implements Cloneable -{ - private String name; - private long mode, time, size; - private int type; - int filepos; - - /** - * Entry type for normal files. - */ - public static final int FILE = '0'; - - /** - * Entry type for directories. - */ - public static final int DIRECTORY = '5'; - - /** - * Create a new TarEntry for a file of the given name at the - * given position in the file. - * - * @param name filename - * @param pos position in the file in bytes - */ - TarEntry(String name, int pos) { - this.name = name; - mode = 0644; - type = FILE; - filepos = pos; - time = System.currentTimeMillis() / 1000; - } - - /** - * Create a new TarEntry for a file of the given name. - * - * @param name filename - */ - public TarEntry(String name) { - this(name, -1); - } - - /** - * Returns the type of this file, one of FILE, LINK, SYM_LINK, - * CHAR_DEVICE, BLOCK_DEVICE, DIRECTORY or FIFO. - * - * @return file type - */ - public int getFileType() { - return type; - } - - /** - * Returns the mode of the file in UNIX permissions format. - * - * @return file mode - */ - public long getMode() { - return mode; - } - - /** - * Returns the name of the file. - * - * @return filename - */ - public String getName() { - return name; - } - - /** - * Returns the size of the file in bytes. - * - * @return filesize - */ - public long getSize() { - return size; - } - - /** - * Returns the modification time of the file in seconds since January - * 1st 1970. - * - * @return time - */ - public long getTime() { - return time; - } - - /** - * Sets the type of the file, one of FILE, LINK, SYMLINK, CHAR_DEVICE, - * BLOCK_DEVICE, or DIRECTORY. - * - * @param type the file type - */ - public void setFileType(int type) { - this.type = type; - } - - /** - * Sets the mode of the file in UNIX permissions format. - * - * @param mode the mode - */ - public void setMode(long mode) { - this.mode = mode; - } - - /** - * Sets the size of the file in bytes. - * - * @param size the file size - */ - public void setSize(long size) { - this.size = size; - } - - /** - * Sets the modification time of the file in seconds since January - * 1st 1970. - * - * @param time the modification time - */ - public void setTime(long time) { - this.time = time; - } -} diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarException.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarException.java deleted file mode 100644 index f67d4097d5..0000000000 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarException.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004, 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 - * Marc-Andre Laperle - Copied to Trace Compass to work around bug 501379 - *******************************************************************************/ - -package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; - -/** - * Exception generated upon encountering corrupted tar files. - */ -public class TarException extends Exception { - /** - * Generated serial version UID for this class. - */ - private static final long serialVersionUID = 2886671254518853528L; - - /** - * Constructs a TarException without a detail string. - */ - public TarException() { - super(); - } - - /** - * Constructs a TarException with the specified detail string. - * - * @param s the detail string - */ - public TarException(String s) { - super(s); - } - - /** - * Constructs a TarException with the specified detail string. - * - * @param s the detail string - * @param cause the cause - */ - public TarException(String s, Throwable cause) { - super(s, cause); - } -} diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFile.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFile.java index 028473adb7..101581d379 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFile.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFile.java @@ -19,7 +19,10 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; -import java.util.zip.GZIPInputStream; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; /** @@ -28,141 +31,150 @@ import java.util.zip.GZIPInputStream; * archive. */ public class TarFile { - private File file; - private TarInputStream entryEnumerationStream; - private TarEntry curEntry; - private TarInputStream entryStream; + private File file; + private TarArchiveInputStream entryEnumerationStream; + private TarArchiveEntry curEntry; + private TarArchiveInputStream entryStream; - private InputStream internalEntryStream; + private InputStream internalEntryStream; // This field is just to prevent try with resources error and keep the code // similar to the original private InputStream fInputStream; - /** - * Create a new TarFile for the given file. - * - * @param file the file - * @throws TarException on Tar error (bad format, etc) - * @throws IOException on i/o error - */ - public TarFile(File file) throws TarException, IOException { - this.file = file; - - fInputStream = new FileInputStream(file); - // First, check if it's a GZIPInputStream. - try { - fInputStream = new GZIPInputStream(fInputStream); - } catch(IOException e) { - //If it is not compressed we close - //the old one and recreate - fInputStream.close(); - fInputStream = new FileInputStream(file); - } - try { - entryEnumerationStream = new TarInputStream(fInputStream); - } catch (TarException | IOException ex) { - fInputStream.close(); - throw ex; - } - curEntry = entryEnumerationStream.getNextEntry(); - } - - /** - * Close the tar file input stream. - * - * @throws IOException if the file cannot be successfully closed - */ - public void close() throws IOException { - if (entryEnumerationStream != null) { + /** + * Create a new TarFile for the given file. + * + * @param file the file + * @throws IOException on i/o error (bad format, etc) + */ + public TarFile(File file) throws IOException { + this.file = file; + + fInputStream = new FileInputStream(file); + // First, check if it's a GZIPInputStream. + try { + fInputStream = new GzipCompressorInputStream(fInputStream); + } catch (IOException e) { + //If it is not compressed we close + //the old one and recreate + fInputStream.close(); + fInputStream = new FileInputStream(file); + } + entryEnumerationStream = new TarArchiveInputStream(fInputStream); + try { + curEntry = (TarArchiveEntry) entryEnumerationStream.getNextEntry(); + } catch (IOException e) { + fInputStream.close(); + throw e; + } + } + + /** + * Close the tar file input stream. + * + * @throws IOException if the file cannot be successfully closed + */ + public void close() throws IOException { + if (entryEnumerationStream != null) { entryEnumerationStream.close(); } - if (internalEntryStream != null) { + if (internalEntryStream != null) { internalEntryStream.close(); } - } - - /** - * Create a new TarFile for the given path name. - * - * @param filename the file name to create the TarFile from - * @throws TarException on Tar error (bad format, etc) - * @throws IOException on i/o error - */ - public TarFile(String filename) throws TarException, IOException { - this(new File(filename)); - } - - /** - * Returns an enumeration cataloguing the tar archive. - * - * @return enumeration of all files in the archive - */ - public Enumeration entries() { - return new Enumeration() { - @Override - public boolean hasMoreElements() { - return (curEntry != null); - } - - @Override - public TarEntry nextElement() { - TarEntry oldEntry = curEntry; - try { - curEntry = entryEnumerationStream.getNextEntry(); - } catch(TarException e) { - curEntry = null; - } catch(IOException e) { - curEntry = null; - } - return oldEntry; - } - }; - } - - /** - * Returns a new InputStream for the given file in the tar archive. - * - * @param entry the entry to get the InputStream from - * @return an input stream for the given file - * @throws TarException on Tar error (bad format, etc) - * @throws IOException on i/o error - */ - public InputStream getInputStream(TarEntry entry) throws TarException, IOException { - if(entryStream == null || !entryStream.skipToEntry(entry)) { - if (internalEntryStream != null) { - internalEntryStream.close(); - } - internalEntryStream = new FileInputStream(file); - // First, check if it's a GZIPInputStream. - try { - internalEntryStream = new GZIPInputStream(internalEntryStream); - } catch(IOException e) { - //If it is not compressed we close - //the old one and recreate - internalEntryStream.close(); - internalEntryStream = new FileInputStream(file); - } - entryStream = new TarInputStream(internalEntryStream, entry) { - @Override - public void close() { - // Ignore close() since we want to reuse the stream. - } - }; - } - return entryStream; - } - - /** - * Returns the path name of the file this archive represents. - * - * @return path - */ - public String getName() { - return file.getPath(); - } - - @Override - protected void finalize() throws Throwable { - close(); - } + } + + /** + * Create a new TarFile for the given path name. + * + * @param filename the file name to create the TarFile from + * @throws IOException on i/o error (bad format, etc) + */ + public TarFile(String filename) throws IOException { + this(new File(filename)); + } + + /** + * Returns an enumeration cataloguing the tar archive. + * + * @return enumeration of all files in the archive + */ + public Enumeration entries() { + return new Enumeration() { + @Override + public boolean hasMoreElements() { + return (curEntry != null); + } + + @Override + public TarArchiveEntry nextElement() { + TarArchiveEntry oldEntry = curEntry; + try { + curEntry = (TarArchiveEntry) entryEnumerationStream.getNextEntry(); + } catch(IOException e) { + curEntry = null; + } + return oldEntry; + } + }; + } + + /** + * Returns a new InputStream for the given file in the tar archive. + * + * @param entry the entry to get the InputStream from + * @return an input stream for the given file + * @throws IOException on i/o error (bad format, etc) + */ + public InputStream getInputStream(TarArchiveEntry entry) throws IOException { + if(entryStream == null || !skipToEntry(entryStream, entry)) { + if (internalEntryStream != null) { + internalEntryStream.close(); + } + internalEntryStream = new FileInputStream(file); + // First, check if it's a GzipCompressorInputStream. + try { + internalEntryStream = new GzipCompressorInputStream(internalEntryStream); + } catch(IOException e) { + //If it is not compressed we close + //the old one and recreate + internalEntryStream.close(); + internalEntryStream = new FileInputStream(file); + } + entryStream = new TarArchiveInputStream(internalEntryStream) { + @Override + public void close() { + // Ignore close() since we want to reuse the stream. + } + }; + skipToEntry(entryStream, entry); + } + return entryStream; + } + + private static boolean skipToEntry(TarArchiveInputStream entryStream, TarArchiveEntry entry) throws IOException { + TarArchiveEntry e = entryStream.getNextTarEntry(); + while (e != null) { + if (e.equals(entry)) { + return true; + } + + e = entryStream.getNextTarEntry(); + } + + return false; + } + + /** + * Returns the path name of the file this archive represents. + * + * @return path + */ + public String getName() { + return file.getPath(); + } + + @Override + protected void finalize() throws Throwable { + close(); + } } diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFileSystemObject.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFileSystemObject.java index 93db412f19..e93ab02a1c 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFileSystemObject.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFileSystemObject.java @@ -16,6 +16,7 @@ import java.io.File; import java.io.IOException; import java.net.URI; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.URIUtil; @@ -26,10 +27,10 @@ import org.eclipse.core.runtime.URIUtil; */ class TarFileSystemObject implements IFileSystemObject { - private TarEntry fFileSystemObject; + private TarArchiveEntry fFileSystemObject; private String fArchivePath; - TarFileSystemObject(TarEntry fileSystemObject, String archivePath) { + TarFileSystemObject(TarArchiveEntry fileSystemObject, String archivePath) { fFileSystemObject = fileSystemObject; fArchivePath = archivePath; } @@ -71,6 +72,6 @@ class TarFileSystemObject implements IFileSystemObject { @Override public boolean isDirectory() { - return fFileSystemObject.getFileType() == TarEntry.DIRECTORY; + return fFileSystemObject.isDirectory(); } } \ No newline at end of file diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarInputStream.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarInputStream.java deleted file mode 100644 index 06b6347ecc..0000000000 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarInputStream.java +++ /dev/null @@ -1,343 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004, 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 - * Marc-Andre Laperle - Copied to Trace Compass to work around bug 501379 - *******************************************************************************/ -package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.eclipse.ui.internal.wizards.datatransfer.DataTransferMessages; - -/** - * Input stream for reading files in ustar format (tar) compatible - * with the specification in IEEE Std 1003.1-2001. Also supports - * long filenames encoded using the GNU @LongLink extension. - */ -@SuppressWarnings("restriction") -public class TarInputStream extends FilterInputStream -{ - private int nextEntry = 0; - private int nextEOF = 0; - private int filepos = 0; - private long bytesread = 0; - private TarEntry firstEntry = null; - private String longLinkName = null; - - /** - * Creates a new tar input stream on the given input stream. - * - * @param in input stream - * @throws TarException on Tar error (bad format, etc) - * @throws IOException on i/o error - */ - public TarInputStream(InputStream in) throws TarException, IOException { - super(in); - - // Read in the first TarEntry to make sure - // the input is a valid tar file stream. - firstEntry = getNextEntry(); - } - - /** - * Create a new tar input stream, skipping ahead to the given entry - * in the file. - * - * @param in input stream - * @param entry skips to this entry in the file - * @throws TarException - * @throws IOException - */ - TarInputStream(InputStream in, TarEntry entry) throws TarException, IOException { - super(in); - skipToEntry(entry); - } - - /** - * The checksum of a tar file header is simply the sum of the bytes in - * the header. - * - * @param header - * @return checksum - */ - private static long headerChecksum(byte[] header) { - long sum = 0; - for(int i = 0; i < 512; i++) { - sum += header[i] & 0xff; - } - return sum; - } - - /** - * Skips ahead to the position of the given entry in the file. - * - * @param entry - * @returns false if the entry has already been passed - * @throws TarException - * @throws IOException - */ - boolean skipToEntry(TarEntry entry) throws TarException, IOException { - long bytestoskip = entry.filepos - bytesread; - if(bytestoskip < 0) { - return false; - } - while(bytestoskip > 0) { - long ret = in.skip(bytestoskip); - if(ret < 0) { - throw new IOException("early end of stream"); //$NON-NLS-1$ - } - bytestoskip -= ret; - bytesread += ret; - } - filepos = entry.filepos; - nextEntry = 0; - nextEOF = 0; - // Read next header to seek to file data. - getNextEntry(); - return true; - } - - /** - * Returns true if the header checksum is correct. - * - * @param header - * @return true if this header has a valid checksum - */ - private static boolean isValidTarHeader(byte[] header) { - long fileChecksum, calculatedChecksum; - int pos, i; - - pos = 148; - StringBuffer checksumString = new StringBuffer(); - for(i = 0; i < 8; i++) { - if(header[pos + i] == ' ') { - continue; - } - if(header[pos + i] == 0 || !Character.isDigit((char) header[pos + i])) { - break; - } - checksumString.append((char) header[pos + i]); - } - if(checksumString.length() == 0) { - return false; - } - if(checksumString.charAt(0) != '0') { - checksumString.insert(0, '0'); - } - try { - fileChecksum = Long.decode(checksumString.toString()).longValue(); - } catch(NumberFormatException exception) { - //This is not valid if it cannot be parsed - return false; - } - - // Blank out the checksum. - for(i = 0; i < 8; i++) { - header[pos + i] = ' '; - } - calculatedChecksum = headerChecksum(header); - - return (fileChecksum == calculatedChecksum); - } - - /** - * Returns the next entry in the tar file. Does not handle - * GNU @LongLink extensions. - * - * @return the next entry in the tar file - * @throws TarException - * @throws IOException - */ - TarEntry getNextEntryInternal() throws TarException, IOException { - byte[] header = new byte[512]; - int pos = 0; - int i; - - if(firstEntry != null) { - TarEntry entryReturn = firstEntry; - firstEntry = null; - return entryReturn; - } - - while(nextEntry > 0) { - long ret = in.skip(nextEntry); - if(ret < 0) { - throw new IOException("early end of stream"); //$NON-NLS-1$ - } - nextEntry -= ret; - bytesread += ret; - } - - int bytestoread = 512; - while(bytestoread > 0) { - int ret = super.read(header, 512 - bytestoread, bytestoread); - if( ret < 0 ) { - throw new IOException("early end of stream"); //$NON-NLS-1$ - } - bytestoread -= ret; - bytesread += ret; - } - - // If we have a header of all zeros, this marks the end of the file. - if(headerChecksum(header) == 0) { - // We are at the end of the file. - if(filepos > 0) { - return null; - } - - // Invalid stream. - throw new TarException("not in tar format"); //$NON-NLS-1$ - } - - // Validate checksum. - if(!isValidTarHeader(header)) { - throw new TarException("not in tar format"); //$NON-NLS-1$ - } - - while (pos < 100 && header[pos] != 0) { - pos++; - } - String name = new String(header, 0, pos, "UTF8"); //$NON-NLS-1$ - // Prepend the prefix here. - pos = 345; - if(header[pos] != 0) { - while (pos < 500 && header[pos] != 0) { - pos++; - } - String prefix = new String(header, 345, pos - 345, "UTF8"); //$NON-NLS-1$ - name = prefix + "/" + name; //$NON-NLS-1$ - } - - TarEntry entry; - if(longLinkName != null) { - entry = new TarEntry(longLinkName, filepos); - longLinkName = null; - } else { - entry = new TarEntry(name, filepos); - } - if(header[156] != 0) { - entry.setFileType(header[156]); - } - - pos = 100; - StringBuffer mode = new StringBuffer(); - for(i = 0; i < 8; i++) { - if(header[pos + i] == 0) { - break; - } - if(header[pos + i] == ' ') { - continue; - } - mode.append((char) header[pos + i]); - } - if(mode.length() > 0 && mode.charAt(0) != '0') { - mode.insert(0, '0'); - } - try { - long fileMode = Long.decode(mode.toString()).longValue(); - entry.setMode(fileMode); - } catch(NumberFormatException nfe) { - throw new TarException(DataTransferMessages.TarImport_invalid_tar_format, nfe); - } - - pos = 100 + 24; - StringBuffer size = new StringBuffer(); - for(i = 0; i < 12; i++) { - if(header[pos + i] == 0) { - break; - } - if(header[pos + i] == ' ') { - continue; - } - size.append((char) header[pos + i]); - } - if(size.charAt(0) != '0') { - size.insert(0, '0'); - } - int fileSize; - try { - fileSize = Integer.decode(size.toString()).intValue(); - } catch(NumberFormatException nfe) { - throw new TarException(DataTransferMessages.TarImport_invalid_tar_format, nfe); - } - - entry.setSize(fileSize); - nextEOF = fileSize; - if(fileSize % 512 > 0) { - nextEntry = fileSize + (512 - (fileSize % 512)); - } else { - nextEntry = fileSize; - } - filepos += (nextEntry + 512); - return entry; - } - - /** - * Moves ahead to the next file in the tar archive and returns - * a TarEntry object describing it. - * - * @return the next entry in the tar file - * @throws TarException on Tar error (bad format, etc) - * @throws IOException on i/o errors - */ - public TarEntry getNextEntry() throws TarException, IOException { - TarEntry entry = getNextEntryInternal(); - - if(entry != null && entry.getName().equals("././@LongLink")) { //$NON-NLS-1$ - // This is a GNU extension for doing long filenames. - // We get a file called ././@LongLink which just contains - // the real pathname. - byte[] longNameData = new byte[(int) entry.getSize()]; - int nbytesread = 0; - while (nbytesread < longNameData.length) { - int cur = read(longNameData, nbytesread, longNameData.length - nbytesread); - if (cur < 0) { - throw new IOException("early end of stream"); //$NON-NLS-1$ - } - nbytesread += cur; - } - - int pos = 0; - while (pos < longNameData.length && longNameData[pos] != 0) { - pos++; - } - longLinkName = new String(longNameData, 0, pos, "UTF8"); //$NON-NLS-1$ - return getNextEntryInternal(); - } - return entry; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - int lenToRead = len; - if(nextEOF == 0) { - return -1; - } - if(lenToRead > nextEOF) { - lenToRead = nextEOF; - } - int size = super.read(b, off, lenToRead); - nextEntry -= size; - nextEOF -= size; - bytesread += size; - return size; - } - - @Override - public int read() throws IOException { - byte[] data = new byte[1]; - int size = read(data, 0, 1); - if (size < 0) { - return size; - } - return data[0]; - } -} diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarLeveledStructureProvider.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarLeveledStructureProvider.java index 6856578652..4ecdad54f0 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarLeveledStructureProvider.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarLeveledStructureProvider.java @@ -11,6 +11,7 @@ * IImportStructureProvider to ILeveledImportStructureProvider * Mickael Istria (Red Hat Inc.) - Bug 486901 * Marc-Andre Laperle - Copied to Trace Compass to work around bug 501379 + * Marc-Andre Laperle - Adapted to use Apache Common Compress *******************************************************************************/ package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; @@ -22,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; @@ -39,11 +41,11 @@ public class TarLeveledStructureProvider implements ILeveledImportStructureProvider { private TarFile tarFile; - private TarEntry root = new TarEntry("/");//$NON-NLS-1$ + private TarArchiveEntry root = new TarArchiveEntry("/", true);//$NON-NLS-1$ - private Map> children; + private Map> children; - private Map directoryEntryCache = new HashMap<>(); + private Map directoryEntryCache = new HashMap<>(); private int stripLevel; @@ -57,35 +59,36 @@ public class TarLeveledStructureProvider implements public TarLeveledStructureProvider(TarFile sourceFile) { super(); tarFile = sourceFile; - root.setFileType(TarEntry.DIRECTORY); } /** * Creates a new container tar entry with the specified name, iff it has * not already been created. If the parent of the given element does not * already exist it will be recursively created as well. - * @param pathname The path representing the container + * @param pathName The path representing the container * @return The element represented by this pathname (it may have already existed) */ - protected TarEntry createContainer(IPath pathname) { - TarEntry existingEntry = directoryEntryCache.get(pathname); + protected TarArchiveEntry createContainer(IPath pathName) { + IPath newPathName = pathName; + TarArchiveEntry existingEntry = directoryEntryCache.get(newPathName); if (existingEntry != null) { return existingEntry; } - TarEntry parent; - if (pathname.segmentCount() == 1) { + TarArchiveEntry parent; + if (newPathName.segmentCount() == 1) { parent = root; } else { - parent = createContainer(pathname.removeLastSegments(1)); + parent = createContainer(newPathName.removeLastSegments(1)); } - TarEntry newEntry = new TarEntry(pathname.toString()); - newEntry.setFileType(TarEntry.DIRECTORY); - directoryEntryCache.put(pathname, newEntry); - List childList = new ArrayList<>(); + // Add trailing / so that the entry knows it's a folder + newPathName = newPathName.addTrailingSeparator(); + TarArchiveEntry newEntry = new TarArchiveEntry(newPathName.toString()); + directoryEntryCache.put(newPathName, newEntry); + List childList = new ArrayList<>(); children.put(newEntry, childList); - List parentChildList = children.get(parent); + List parentChildList = children.get(parent); NonNullUtils.checkNotNull(parentChildList).add(newEntry); return newEntry; } @@ -94,9 +97,9 @@ public class TarLeveledStructureProvider implements * Creates a new tar file entry with the specified name. * @param entry the entry to create the file for */ - protected void createFile(TarEntry entry) { + protected void createFile(TarArchiveEntry entry) { IPath pathname = new Path(entry.getName()); - TarEntry parent; + TarArchiveEntry parent; if (pathname.segmentCount() == 1) { parent = root; } else { @@ -104,7 +107,7 @@ public class TarLeveledStructureProvider implements .removeLastSegments(1)); } - List childList = children.get(parent); + List childList = children.get(parent); NonNullUtils.checkNotNull(childList).add(entry); } @@ -120,10 +123,7 @@ public class TarLeveledStructureProvider implements @Override public InputStream getContents(Object element) { try { - return tarFile.getInputStream((TarEntry) element); - } catch (TarException e) { - IDEWorkbenchPlugin.log(e.getLocalizedMessage(), e); - return null; + return tarFile.getInputStream((TarArchiveEntry) element); } catch (IOException e) { IDEWorkbenchPlugin.log(e.getLocalizedMessage(), e); return null; @@ -138,7 +138,7 @@ public class TarLeveledStructureProvider implements */ public ResourceAttributes getResourceAttributes(Object element) { ResourceAttributes attributes = new ResourceAttributes(); - TarEntry entry = (TarEntry) element; + TarArchiveEntry entry = (TarArchiveEntry) element; attributes.setExecutable((entry.getMode() & 0100) != 0); attributes.setReadOnly((entry.getMode() & 0200) == 0); return attributes; @@ -146,24 +146,24 @@ public class TarLeveledStructureProvider implements @Override public String getFullPath(Object element) { - String name = stripPath(((TarEntry) element).getName()); + String name = stripPath(((TarArchiveEntry) element).getName()); return ArchiveUtil.toValidNamesPath(name).toOSString(); } @Override public String getLabel(Object element) { if (element.equals(root)) { - return ((TarEntry) element).getName(); + return ((TarArchiveEntry) element).getName(); } - String name = ((TarEntry) element).getName(); + String name = ((TarArchiveEntry) element).getName(); return stripPath(ArchiveUtil.toValidNamesPath(name).lastSegment()); } /** * Returns the entry that this importer uses as the root sentinel. * - * @return TarEntry entry + * @return TarArchiveEntry entry */ @Override public Object getRoot() { @@ -199,12 +199,12 @@ public class TarLeveledStructureProvider implements children = new HashMap<>(1000); children.put(root, new ArrayList<>()); - Enumeration entries = tarFile.entries(); + Enumeration entries = tarFile.entries(); while (entries.hasMoreElements()) { - TarEntry entry = entries.nextElement(); + TarArchiveEntry entry = entries.nextElement(); IPath path = new Path(entry.getName()).addTrailingSeparator(); - if (entry.getFileType() == TarEntry.DIRECTORY) { + if (entry.isDirectory()) { createContainer(path); } else { @@ -221,7 +221,7 @@ public class TarLeveledStructureProvider implements @Override public boolean isFolder(Object element) { - return (((TarEntry) element).getFileType() == TarEntry.DIRECTORY); + return (((TarArchiveEntry) element).isDirectory()); } /* diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TraceValidateAndImportOperation.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TraceValidateAndImportOperation.java index 93b1ee9d67..6a90871db1 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TraceValidateAndImportOperation.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TraceValidateAndImportOperation.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -133,6 +134,51 @@ public class TraceValidateAndImportOperation extends TmfWorkspaceModifyOperation fSelectedFileSystemElements = traceFileSystemElements; } + /** + * This orders by files first then the folders. Then by lexical order. + * This comparator handles full paths. + * Example of ordering: + * + * /trace.txt + * /folderA/trace.txt + * /folderA/folderB/trace.txt + * /folderZ/trace.txt + */ + private final class FileObjectPathComparator implements Comparator { + @Override + public int compare(TraceFileSystemElement e1, TraceFileSystemElement e2) { + IFileSystemObject o1 = e1.getFileSystemObject(); + IFileSystemObject o2 = e2.getFileSystemObject(); + IPath p1 = new Path(e1.getProvider().getFullPath(o1)); + IPath p2 = new Path(e2.getProvider().getFullPath(o2)); + int segmentCount1 = p1.segmentCount(); + int segmentCount2 = p2.segmentCount(); + + int commonParentSegmentCount = Math.min(segmentCount1, segmentCount2) - 1; + // Compare parents that are common (in terms of segment number). + // If one of them is different, we do not need to worry about any + // children, we already know in which order they are going to be. + for (int i = 0; i < commonParentSegmentCount; i++) { + int compare = p1.segment(i).compareToIgnoreCase(p2.segment(i)); + if (compare != 0) { + return compare; + } + } + + // At this point, we know all the common parent folders are the same. + // Either: + // - One of them is shorter which means it should be processed first because files are processed before sub-folders. + // or + // - They are the same level so only the name matters. + if (segmentCount1 != segmentCount2) { + return Integer.compare(segmentCount1, segmentCount2); + } + + // + return o1.getName().compareToIgnoreCase(o2.getName()); + } + } + @Override protected void execute(IProgressMonitor progressMonitor) throws CoreException, InvocationTargetException, InterruptedException { try { @@ -246,6 +292,10 @@ public class TraceValidateAndImportOperation extends TmfWorkspaceModifyOperation private void importFileSystemElements(IProgressMonitor monitor, List fileSystemElements) throws InterruptedException, TmfTraceImportException, CoreException, InvocationTargetException { SubMonitor subMonitor = SubMonitor.convert(monitor, fileSystemElements.size()); + // Sort the elements in a sensible order to make it more predictable to + // the user when there can be name clashes. Otherwise, the order can + // seem pretty random depending on the OS/Filesystem. + fileSystemElements.sort(new FileObjectPathComparator()); ListIterator fileSystemElementsIter = fileSystemElements.listIterator(); diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ZipFileSystemObject.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ZipFileSystemObject.java index d0204f8cda..2f6822d31d 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ZipFileSystemObject.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ZipFileSystemObject.java @@ -15,8 +15,8 @@ package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; import java.io.File; import java.io.IOException; import java.net.URI; -import java.util.zip.ZipEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.URIUtil; @@ -26,10 +26,10 @@ import org.eclipse.core.runtime.URIUtil; */ class ZipFileSystemObject implements IFileSystemObject { - private ZipEntry fFileSystemObject; + private ZipArchiveEntry fFileSystemObject; private String fArchivePath; - ZipFileSystemObject(ZipEntry fileSystemObject, String archivePath) { + ZipFileSystemObject(ZipArchiveEntry fileSystemObject, String archivePath) { fFileSystemObject = fileSystemObject; fArchivePath = archivePath; } diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ZipLeveledStructureProvider.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ZipLeveledStructureProvider.java new file mode 100644 index 0000000000..1e2bba9453 --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ZipLeveledStructureProvider.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (c) 2000, 2016 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 - Was ZipFileStructureProvider, performed changes from + * IImportStructureProvider to ILeveledImportStructureProvider + * Mickael Istria (Red Hat Inc.) - Bug 486901 + * Marc-Andre Laperle - Copied to Trace + * Compass to use Apache Common Compress and fix bug 501664 + *******************************************************************************/ +package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.common.core.NonNullUtils; +import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; +import org.eclipse.ui.internal.wizards.datatransfer.DataTransferMessages; +import org.eclipse.ui.internal.wizards.datatransfer.ILeveledImportStructureProvider; + +/** + * This class provides information regarding the context structure and content + * of specified zip file entry objects. + * + * This structure provider also makes sure to return safe paths. For example, + * if a Zip entry contains a ':' and is extracted on Windows, it will be changed + * to a '_' + */ +@SuppressWarnings("restriction") +public class ZipLeveledStructureProvider implements + ILeveledImportStructureProvider { + private ZipFile zipFile; + + private ZipArchiveEntry root = new ZipArchiveEntry("/");//$NON-NLS-1$ + + private Map> children; + + private Map directoryEntryCache = new HashMap<>(); + + private int stripLevel; + + /** + * Creates a ZipFileStructureProvider, which will operate on + * the passed zip file. + * + * @param sourceFile + * The source file to create the ZipLeveledStructureProvider + * around + */ + public ZipLeveledStructureProvider(ZipFile sourceFile) { + super(); + zipFile = sourceFile; + stripLevel = 0; + } + + /** + * Creates a new container zip entry with the specified name, iff it has + * not already been created. If the parent of the given element does not + * already exist it will be recursively created as well. + * @param pathname The path representing the container + * @return The element represented by this pathname (it may have already existed) + */ + protected ZipArchiveEntry createContainer(IPath pathname) { + ZipArchiveEntry existingEntry = directoryEntryCache.get(pathname); + if (existingEntry != null) { + return existingEntry; + } + + ZipArchiveEntry parent; + if (pathname.segmentCount() == 0) { + return null; + } else if (pathname.segmentCount() == 1) { + parent = root; + } else { + parent = createContainer(pathname.removeLastSegments(1)); + } + ZipArchiveEntry newEntry = new ZipArchiveEntry(pathname.toString()); + directoryEntryCache.put(pathname, newEntry); + List childList = new ArrayList<>(); + children.put(newEntry, childList); + + List parentChildList = children.get(parent); + NonNullUtils.checkNotNull(parentChildList).add(newEntry); + return newEntry; + } + + /** + * Creates a new file zip entry with the specified name. + * @param entry the entry to create the file for + */ + protected void createFile(ZipArchiveEntry entry) { + IPath pathname = new Path(entry.getName()); + ZipArchiveEntry parent; + if (pathname.segmentCount() == 1) { + parent = root; + } else { + parent = directoryEntryCache.get(pathname + .removeLastSegments(1)); + } + + @Nullable List childList = children.get(parent); + NonNullUtils.checkNotNull(childList).add(entry); + } + + @Override + public List getChildren(Object element) { + if (children == null) { + initialize(); + } + + return (children.get(element)); + } + + @Override + public InputStream getContents(Object element) { + try { + return zipFile.getInputStream((ZipArchiveEntry) element); + } catch (IOException e) { + IDEWorkbenchPlugin.log(e.getLocalizedMessage(), e); + return null; + } + } + + /* + * Strip the leading directories from the path + */ + private String stripPath(String path) { + String strippedPath = path; + String pathOrig = strippedPath; + for (int i = 0; i < stripLevel; i++) { + int firstSep = strippedPath.indexOf('/'); + // If the first character was a separator we must strip to the next + // separator as well + if (firstSep == 0) { + strippedPath = strippedPath.substring(1); + firstSep = strippedPath.indexOf('/'); + } + // No separator was present so we're in a higher directory right + // now + if (firstSep == -1) { + return pathOrig; + } + strippedPath = strippedPath.substring(firstSep); + } + return strippedPath; + } + + @Override + public String getFullPath(Object element) { + String name = ((ZipArchiveEntry) element).getName(); + return ArchiveUtil.toValidNamesPath(name).toOSString(); + } + + @Override + public String getLabel(Object element) { + if (element.equals(getRoot())) { + return ((ZipArchiveEntry) element).getName(); + } + String name = ((ZipArchiveEntry) element).getName(); + return stripPath(ArchiveUtil.toValidNamesPath(name).lastSegment()); + } + + /** + * Returns the entry that this importer uses as the root sentinel. + * + * @return ZipArchiveEntry + */ + @Override + public Object getRoot() { + return root; + } + + /** + * Returns the zip file that this provider provides structure for. + * + * @return The zip file + */ + public ZipFile getZipFile() { + return zipFile; + } + + + @Override + public boolean closeArchive(){ + try { + getZipFile().close(); + } catch (IOException e) { + IDEWorkbenchPlugin.log(DataTransferMessages.ZipImport_couldNotClose + + getZipFile(), e); + return false; + } + return true; + } + + /** + * Initializes this object's children table based on the contents of the + * specified source file. + */ + protected void initialize() { + children = new HashMap<>(1000); + + children.put(root, new ArrayList<>()); + Enumeration entries = zipFile.getEntries(); + while (entries.hasMoreElements()) { + ZipArchiveEntry entry = entries.nextElement(); + IPath path = new Path(entry.getName()).addTrailingSeparator(); + + if (entry.isDirectory()) { + createContainer(path); + } else + { + // Ensure the container structure for all levels above this is initialized + // Once we hit a higher-level container that's already added we need go no further + int pathSegmentCount = path.segmentCount(); + if (pathSegmentCount > 1) { + createContainer(path.uptoSegment(pathSegmentCount - 1)); + } + createFile(entry); + } + } + } + + @Override + public boolean isFolder(Object element) { + return ((ZipArchiveEntry) element).isDirectory(); + } + + @Override + public void setStrip(int level) { + stripLevel = level; + } + + @Override + public int getStrip() { + return stripLevel; + } +} diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/AbstractTracePackageOperation.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/AbstractTracePackageOperation.java index baace2bb96..756e422d9e 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/AbstractTracePackageOperation.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/AbstractTracePackageOperation.java @@ -12,18 +12,18 @@ package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.Vector; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TarEntry; -import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TarException; import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TarFile; /** @@ -120,15 +120,20 @@ public abstract class AbstractTracePackageOperation { return null; } + File file = new File(fFileName); + if (file.isDirectory()) { + return null; + } + try { - return new ZipArchiveFile(new ZipFile(fFileName)); + return new ZipArchiveFile(new ZipFile(file)); } catch (IOException e) { // ignore } try { - return new TarArchiveFile(new TarFile(fFileName)); - } catch (TarException | IOException e) { + return new TarArchiveFile(new TarFile(file)); + } catch (IOException e) { // ignore } @@ -176,7 +181,7 @@ public abstract class AbstractTracePackageOperation { } /** - * Common interface between ZipEntry and TarEntry + * Common interface between ZipArchiveEntry and TarArchiveEntry */ protected interface ArchiveEntry { /** @@ -211,10 +216,9 @@ public abstract class AbstractTracePackageOperation { * @param entry * the given file * @return an input stream for the given file - * @throws TarException * @throws IOException */ - InputStream getInputStream(ArchiveEntry entry) throws TarException, IOException; + InputStream getInputStream(ArchiveEntry entry) throws IOException; } /** @@ -238,7 +242,7 @@ public abstract class AbstractTracePackageOperation { public Enumeration<@NonNull ? extends ArchiveEntry> entries() { Vector<@NonNull ArchiveEntry> v = new Vector<>(); for (Enumeration e = fTarFile.entries(); e.hasMoreElements();) { - v.add(new TarArchiveEntry((TarEntry) e.nextElement())); + v.add(new TarArchiveEntryAdapter((TarArchiveEntry) e.nextElement())); } return v.elements(); @@ -250,24 +254,24 @@ public abstract class AbstractTracePackageOperation { } @Override - public InputStream getInputStream(ArchiveEntry entry) throws TarException, IOException { - return fTarFile.getInputStream(((TarArchiveEntry) entry).getTarEntry()); + public InputStream getInputStream(ArchiveEntry entry) throws IOException { + return fTarFile.getInputStream(((TarArchiveEntryAdapter) entry).getTarEntry()); } } /** - * Adapter for TarEntry to ArchiveEntry + * Adapter for TarArchiveEntry to ArchiveEntry */ - protected class TarArchiveEntry implements ArchiveEntry { - private TarEntry fTarEntry; + protected class TarArchiveEntryAdapter implements ArchiveEntry { + private TarArchiveEntry fTarEntry; /** - * Constructs a TarArchiveEntry for a TarEntry + * Constructs a TarArchiveEntry for a TarArchiveEntry * * @param tarEntry - * the TarEntry + * the TarArchiveEntry */ - public TarArchiveEntry(TarEntry tarEntry) { + public TarArchiveEntryAdapter(TarArchiveEntry tarEntry) { this.fTarEntry = tarEntry; } @@ -277,11 +281,11 @@ public abstract class AbstractTracePackageOperation { } /** - * Get the corresponding TarEntry + * Get the corresponding TarArchiveEntry * - * @return the corresponding TarEntry + * @return the corresponding TarArchiveEntry */ - public TarEntry getTarEntry() { + public TarArchiveEntry getTarEntry() { return fTarEntry; } @@ -294,17 +298,17 @@ public abstract class AbstractTracePackageOperation { /** * Adapter for ArchiveEntry to ArchiveEntry */ - protected class ZipAchiveEntry implements ArchiveEntry { + protected class ZipAchiveEntryAdapter implements ArchiveEntry { - private ZipEntry fZipEntry; + private ZipArchiveEntry fZipEntry; /** - * Constructs a ZipAchiveEntry for a ZipEntry + * Constructs a ZipAchiveEntryAdapter for a ZipArchiveEntry * * @param zipEntry - * the ZipEntry + * the ZipArchiveEntry */ - public ZipAchiveEntry(ZipEntry zipEntry) { + public ZipAchiveEntryAdapter(ZipArchiveEntry zipEntry) { this.fZipEntry = zipEntry; } @@ -314,11 +318,11 @@ public abstract class AbstractTracePackageOperation { } /** - * Get the corresponding ZipEntry + * Get the corresponding ZipArchiveEntry * - * @return the corresponding ZipEntry + * @return the corresponding ZipArchiveEntry */ - public ZipEntry getZipEntry() { + public ZipArchiveEntry getZipEntry() { return fZipEntry; } @@ -348,8 +352,8 @@ public abstract class AbstractTracePackageOperation { @Override public Enumeration<@NonNull ? extends ArchiveEntry> entries() { Vector<@NonNull ArchiveEntry> v = new Vector<>(); - for (Enumeration e = fZipFile.entries(); e.hasMoreElements();) { - v.add(new ZipAchiveEntry((ZipEntry) e.nextElement())); + for (Enumeration e = fZipFile.getEntries(); e.hasMoreElements();) { + v.add(new ZipAchiveEntryAdapter(e.nextElement())); } return v.elements(); @@ -361,8 +365,8 @@ public abstract class AbstractTracePackageOperation { } @Override - public InputStream getInputStream(ArchiveEntry entry) throws TarException, IOException { - return fZipFile.getInputStream(((ZipAchiveEntry) entry).getZipEntry()); + public InputStream getInputStream(ArchiveEntry entry) throws IOException { + return fZipFile.getInputStream(((ZipAchiveEntryAdapter) entry).getZipEntry()); } } diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/importexport/TracePackageImportOperation.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/importexport/TracePackageImportOperation.java index 8f5cad1576..acf6229f46 100644 --- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/importexport/TracePackageImportOperation.java +++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/importexport/TracePackageImportOperation.java @@ -42,7 +42,6 @@ import org.eclipse.core.runtime.URIUtil; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jface.operation.ModalContext; import org.eclipse.tracecompass.internal.tmf.ui.Activator; -import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TarException; import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.AbstractTracePackageOperation; import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageBookmarkElement; import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageElement; @@ -107,8 +106,6 @@ public class TracePackageImportOperation extends AbstractTracePackageOperation i inputStream = ((ArchiveProviderElement) element).getContents(); } catch (IOException e) { fException = e; - } catch (TarException e) { - fException = e; } return inputStream; } @@ -148,7 +145,7 @@ public class TracePackageImportOperation extends AbstractTracePackageOperation i this.fEntry = entry; } - public InputStream getContents() throws TarException, IOException { + public InputStream getContents() throws IOException { return fArchiveFile.getInputStream(fEntry); } -- 2.34.1