blob: 4a82abfe8f97951ac2a4ca28deacef5c1af449f4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2017 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Marc-Andre Laperle - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace;
import java.io.File;
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;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.project.operations.TmfWorkspaceModifyOperation;
import org.eclipse.tracecompass.internal.util.ByteBufferTracker;
import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceImportException;
import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType;
import org.eclipse.tracecompass.tmf.core.project.model.TraceTypeHelper;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.core.util.Pair;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceFolder;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceTypeUIUtils;
import org.eclipse.tracecompass.tmf.ui.project.model.TraceUtils;
import org.eclipse.ui.dialogs.FileSystemElement;
import org.eclipse.ui.dialogs.IOverwriteQuery;
import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider;
import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
import org.eclipse.ui.wizards.datatransfer.ImportOperation;
/**
* An operation that performs validation and importing of traces. Its primary
* inputs are a collection of TraceFileSystemElement and several flags that
* control
*
*/
public class TraceValidateAndImportOperation extends TmfWorkspaceModifyOperation {
private static final String TRACE_IMPORT_TEMP_FOLDER = ".traceImport"; //$NON-NLS-1$
/**
* Folder where we extract the archive when we want to keep it.
*/
private static final String EXTRACTED_ARCHIVE_FOLDER = ".extractedArchive"; //$NON-NLS-1$
private static final @NonNull String DEFAULT_ARCHIVE_FOLDER_NAME = "extractedTrace"; //$NON-NLS-1$
private String fTraceType;
private IPath fDestinationContainerPath;
private IPath fBaseSourceContainerPath;
private boolean fImportFromArchive;
private int fImportOptionFlags;
private Shell fShell;
private TmfTraceFolder fTraceFolderElement;
private List<TraceFileSystemElement> fSelectedFileSystemElements;
private ITmfTimestamp fStartTimeRange;
private ITmfTimestamp fEndTimeRange;
private @NonNull String fFolderName;
private boolean fKeepArchive;
private IStatus fStatus;
private ImportConflictHandler fConflictHandler;
private String fCurrentPath;
private List<IResource> fImportedResources;
/**
* Constructs a new validate and import operation.
*
* @param shell
* the parent shell to use for possible error messages
* @param traceFileSystemElements
* the trace file elements to import
* @param traceId
* the trace type to import the traces as (can be set to null for
* automatic detection)
* @param baseSourceContainerPath
* the path to the container of the source. This is used as a "base"
* to generate the folder structure for the "preserve folder
* structure" option.
* @param destinationContainerPath
* the destination path of the import operation, typically a trace
* folder path.
* @param importFromArchive
* whether or not the source is an archive
* @param importOptionFlags
* bit-wise 'or' of import option flag constants (
* {@link ImportTraceWizardPage#OPTION_PRESERVE_FOLDER_STRUCTURE} ,
* {@link ImportTraceWizardPage#OPTION_CREATE_LINKS_IN_WORKSPACE} ,
* {@link ImportTraceWizardPage#OPTION_IMPORT_UNRECOGNIZED_TRACES} ,
* and
* {@link ImportTraceWizardPage#OPTION_OVERWRITE_EXISTING_RESOURCES}
* )
* @param traceFolderElement
* the destination trace folder of the import operation.
* @param startTimeRange
* Timestamp representing the start time of the range
* @param endTimeRange
* Timestamp representing the end time of the range
* @param folderName
* Archive folder name
* @param keepArchive
* whether or not the extracted archive is kept
*/
public TraceValidateAndImportOperation(Shell shell, List<TraceFileSystemElement> traceFileSystemElements, String traceId, IPath baseSourceContainerPath, IPath destinationContainerPath, boolean importFromArchive, int importOptionFlags,
TmfTraceFolder traceFolderElement, ITmfTimestamp startTimeRange, ITmfTimestamp endTimeRange, String folderName, boolean keepArchive) {
fTraceType = traceId;
fBaseSourceContainerPath = baseSourceContainerPath;
fDestinationContainerPath = destinationContainerPath;
fImportOptionFlags = importOptionFlags;
fImportFromArchive = importFromArchive;
fShell = shell;
fTraceFolderElement = traceFolderElement;
fStartTimeRange = startTimeRange;
fEndTimeRange = endTimeRange;
fFolderName = folderName != null ? folderName : DEFAULT_ARCHIVE_FOLDER_NAME;
fKeepArchive = keepArchive;
boolean overwriteExistingResources = (importOptionFlags & ImportTraceWizardPage.OPTION_OVERWRITE_EXISTING_RESOURCES) != 0;
if (overwriteExistingResources) {
setConflictHandler(new ImportConflictHandler(fShell, fTraceFolderElement, ImportConfirmation.OVERWRITE_ALL));
} else {
setConflictHandler(new ImportConflictHandler(fShell, fTraceFolderElement, ImportConfirmation.SKIP));
}
fImportedResources = new ArrayList<>();
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<TraceFileSystemElement> {
@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 {
final int ARCHIVE_OR_DIRECTORY_PROGRESS = 45;
final int EXTRA_IMPORT_OPERATION_PROGRESS = 45;
final int DELETE_PROGRESS = 10;
int TOTAL_PROGRESS = ARCHIVE_OR_DIRECTORY_PROGRESS +
EXTRA_IMPORT_OPERATION_PROGRESS + DELETE_PROGRESS;
final List<TraceFileSystemElement> selectedFileSystemElements = fSelectedFileSystemElements;
// List fileSystemElements will be filled using the
// passThroughFilter
SubMonitor subMonitor = SubMonitor.convert(progressMonitor, TOTAL_PROGRESS);
// Check if operation was cancelled.
ModalContext.checkCanceled(subMonitor);
// Temporary directory to contain any extracted files
IFolder destTempFolder = fTraceFolderElement.getProject().getResource().getFolder(TRACE_IMPORT_TEMP_FOLDER);
if (destTempFolder.exists()) {
SubMonitor monitor = subMonitor.newChild(1);
destTempFolder.delete(true, monitor);
}
SubMonitor monitor = subMonitor.newChild(1);
destTempFolder.create(IResource.HIDDEN, true, monitor);
String baseSourceLocation = null;
if (fImportFromArchive) {
// When importing from archive, we first extract the
// *selected* files to a temporary folder then create new
// TraceFileSystemElements
SubMonitor archiveMonitor = SubMonitor.convert(subMonitor.newChild(ARCHIVE_OR_DIRECTORY_PROGRESS), 2);
if (fKeepArchive) {
fImportOptionFlags |= ImportTraceWizardPage.OPTION_KEEP_EXTRACTED_ARCHIVE;
fImportOptionFlags |= ImportTraceWizardPage.OPTION_CREATE_LINKS_IN_WORKSPACE;
IFolder extractedFolder = fTraceFolderElement.getProject().getResource().getFolder(EXTRACTED_ARCHIVE_FOLDER);
if(!extractedFolder.exists()) {
monitor = archiveMonitor.newChild(1);
extractedFolder.create(IResource.FOLDER, true, monitor);
}
destTempFolder = extractedFolder.getFolder(fFolderName);
if (!destTempFolder.exists()) {
monitor = archiveMonitor.newChild(1);
destTempFolder.create(IResource.FOLDER, true, monitor);
}
}
// Extract selected files from source archive to temporary
// folder
extractArchiveContent(selectedFileSystemElements.iterator(), destTempFolder, archiveMonitor.newChild(1));
archiveMonitor.subTask(""); //$NON-NLS-1$
if (!selectedFileSystemElements.isEmpty()) {
// Even if the files were extracted to temporary folder, they
// have to look like they originate from the source archive
baseSourceLocation = getRootElement(selectedFileSystemElements.get(0)).getSourceLocation();
if ((fImportOptionFlags & ImportTraceWizardPage.OPTION_SKIP_ARCHIVE_EXTRACTION) == 0) {
// Extract additional archives contained in the extracted files
// (archives in archives)
List<TraceFileSystemElement> tempFolderFileSystemElements = createElementsForFolder(destTempFolder);
extractAllArchiveFiles(tempFolderFileSystemElements, destTempFolder, destTempFolder.getLocation(), archiveMonitor.newChild(1));
}
}
} else {
SubMonitor directoryMonitor = SubMonitor.convert(subMonitor.newChild(ARCHIVE_OR_DIRECTORY_PROGRESS), 2);
// Import selected files, excluding archives (done in a later step)
importFileSystemElements(directoryMonitor.newChild(1), selectedFileSystemElements);
if ((fImportOptionFlags & ImportTraceWizardPage.OPTION_SKIP_ARCHIVE_EXTRACTION) == 0) {
// Extract archives in selected files (if any) to temporary folder
extractAllArchiveFiles(selectedFileSystemElements, destTempFolder, fBaseSourceContainerPath, directoryMonitor.newChild(1));
}
// Even if the files were extracted to temporary folder, they
// have to look like they originate from the source folder
baseSourceLocation = URIUtil.toUnencodedString(fBaseSourceContainerPath.toFile().getCanonicalFile().toURI());
}
/*
* Import extracted files that are now in the temporary folder, if any
*/
// We need to update the source container path because the
// "preserve folder structure" option would create the
// wrong trace folders otherwise.
fBaseSourceContainerPath = destTempFolder.getLocation();
List<TraceFileSystemElement> tempFolderFileSystemElements = createElementsForFolder(destTempFolder);
boolean keepArchive = (fImportOptionFlags & ImportTraceWizardPage.OPTION_KEEP_EXTRACTED_ARCHIVE) != 0;
if (!tempFolderFileSystemElements.isEmpty()) {
calculateSourceLocations(tempFolderFileSystemElements, baseSourceLocation);
// Never import extracted files as links, they would link to the
// temporary directory that will be deleted
if (!keepArchive) {
fImportOptionFlags = fImportOptionFlags & ~ImportTraceWizardPage.OPTION_CREATE_LINKS_IN_WORKSPACE;
}
SubMonitor importTempMonitor = subMonitor.newChild(EXTRA_IMPORT_OPERATION_PROGRESS);
importFileSystemElements(importTempMonitor.newChild(1), tempFolderFileSystemElements);
}
if (destTempFolder.exists() && !keepArchive) {
// Do a gc if the tracker got marked during trace validation
if (ByteBufferTracker.getAndReset()) {
System.gc();
}
destTempFolder.delete(true, subMonitor.newChild(TOTAL_PROGRESS));
}
setStatus(Status.OK_STATUS);
} catch (InterruptedException e) {
setStatus(Status.CANCEL_STATUS);
Thread.currentThread().interrupt();
} catch (Exception e) {
String errorMessage = Messages.ImportTraceWizard_ImportProblem + ": " + //$NON-NLS-1$
(fCurrentPath != null ? fCurrentPath : ""); //$NON-NLS-1$
Activator.getDefault().logError(errorMessage, e);
setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, errorMessage, e));
}
}
private TraceFileSystemElement filterSelectedFileSystemElement(TraceFileSystemElement selectedFileSystemElements, IProgressMonitor monitor) {
if ((fImportOptionFlags & ImportTraceWizardPage.OPTION_FILTER_TIMERANGE) != 0 && fStartTimeRange != null && fEndTimeRange != null) {
return filterByTimerange(selectedFileSystemElements, monitor);
}
return selectedFileSystemElements;
}
private TraceFileSystemElement filterByTimerange(TraceFileSystemElement selectedFileSystemElements, IProgressMonitor monitor) {
SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
subMonitor.subTask(Messages.ImportTraceWizard_FilteringOperationTaskName);
String tracePath = selectedFileSystemElements.getFileSystemObject().getAbsolutePath();
ITmfTrace trace = null;
try {
TraceTypeHelper traceTypeHelper = TmfTraceTypeUIUtils.selectTraceType(tracePath, null, null);
if (traceTypeHelper == null) {
return null;
}
IConfigurationElement ce = TmfTraceType.getTraceAttributes(traceTypeHelper.getTraceTypeId());
if (ce == null) {
return null;
}
trace = (ITmfTrace) ce.createExecutableExtension(TmfTraceType.TRACE_TYPE_ATTR);
ITmfEvent event = (ITmfEvent) ce.createExecutableExtension(TmfTraceType.EVENT_TYPE_ATTR);
trace.initTrace(null, tracePath, event.getClass());
if (isInTimerange(trace)) {
return selectedFileSystemElements;
}
} catch (TmfTraceImportException | CoreException | TmfTraceException e) {
String errorMessage = Messages.ImportTraceWizard_ImportProblem + ": " + tracePath; //$NON-NLS-1$
Activator.getDefault().logError(errorMessage, e);
} finally {
if (trace != null) {
trace.dispose();
TmfTraceManager.deleteSupplementaryFolder(trace);
}
subMonitor.worked(1);
}
return null;
}
private boolean isInTimerange(ITmfTrace trace) {
ITmfTimestamp startTimestamp = trace.readStart();
ITmfTimestamp endTimestamp = trace.readEnd();
return (startTimestamp != null && endTimestamp != null) && (startTimestamp.compareTo(fEndTimeRange) <= 0 && endTimestamp.compareTo(fStartTimeRange) >= 0);
}
/**
* Get the list of resources that were imported by this operation. An
* example use case would be to use this to open traces that were imported
* by this operation.
*
* Note this includes only valid traces and doesn'tinclude unrecognized
* files.
*
* @return the trace resources that were imported
*/
public List<IResource> getImportedResources() {
return fImportedResources;
}
/**
* Import a collection of file system elements into the workspace.
*/
private void importFileSystemElements(IProgressMonitor monitor, List<TraceFileSystemElement> 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<TraceFileSystemElement> fileSystemElementsIter = fileSystemElements.listIterator();
// Map to remember already imported directory traces
final Map<String, TraceFileSystemElement> directoryTraces = new HashMap<>();
while (fileSystemElementsIter.hasNext()) {
ModalContext.checkCanceled(monitor);
fCurrentPath = null;
TraceFileSystemElement element = fileSystemElementsIter.next();
IFileSystemObject fileSystemObject = element.getFileSystemObject();
String resourcePath = element.getFileSystemObject().getAbsolutePath();
element.setDestinationContainerPath(computeDestinationContainerPath(new Path(resourcePath)));
fCurrentPath = resourcePath;
SubMonitor sub = subMonitor.split(1, SubMonitor.SUPPRESS_BEGINTASK | SubMonitor.SUPPRESS_SUBTASK);
if (element.isDirectory()) {
if (!directoryTraces.containsKey(resourcePath) && isDirectoryTrace(element)) {
directoryTraces.put(resourcePath, element);
validateAndImportTrace(element, sub);
}
} else {
TraceFileSystemElement parentElement = (TraceFileSystemElement) element.getParent();
String parentPath = parentElement.getFileSystemObject().getAbsolutePath();
parentElement.setDestinationContainerPath(computeDestinationContainerPath(new Path(parentPath)));
fCurrentPath = parentPath;
if (!directoryTraces.containsKey(parentPath)) {
if (isDirectoryTrace(parentElement)) {
directoryTraces.put(parentPath, parentElement);
validateAndImportTrace(parentElement, sub);
} else {
boolean validateFile = true;
TraceFileSystemElement grandParentElement = (TraceFileSystemElement) parentElement.getParent();
// Special case for LTTng trace that may contain index
// directory and files
if (grandParentElement != null) {
String grandParentPath = grandParentElement.getFileSystemObject().getAbsolutePath();
grandParentElement.setDestinationContainerPath(computeDestinationContainerPath(new Path(parentPath)));
fCurrentPath = grandParentPath;
if (directoryTraces.containsKey(grandParentPath)) {
validateFile = false;
} else if (isDirectoryTrace(grandParentElement)) {
directoryTraces.put(grandParentPath, grandParentElement);
validateAndImportTrace(grandParentElement, sub);
validateFile = false;
}
}
if (validateFile && (fileSystemObject.exists())) {
validateAndImportTrace(element, sub);
}
}
}
}
}
}
/**
* Generate a new list of file system elements for the specified folder.
*/
private static List<TraceFileSystemElement> createElementsForFolder(IFolder folder) {
// Create the new import provider and root element based on the
// specified folder
FileSystemObjectImportStructureProvider importStructureProvider = new FileSystemObjectImportStructureProvider(FileSystemStructureProvider.INSTANCE, null);
IFileSystemObject rootElement = importStructureProvider.getIFileSystemObject(new File(folder.getLocation().toOSString()));
TraceFileSystemElement createRootElement = TraceFileSystemElement.createRootTraceFileElement(rootElement, importStructureProvider);
List<TraceFileSystemElement> list = new LinkedList<>();
createRootElement.getAllChildren(list);
return list;
}
/**
* Extract all file system elements (File) to destination folder (typically
* workspace/TraceProject/.traceImport)
*/
private void extractAllArchiveFiles(List<TraceFileSystemElement> fileSystemElements, IFolder destFolder, IPath baseSourceContainerPath, IProgressMonitor progressMonitor) throws InterruptedException, CoreException, InvocationTargetException {
SubMonitor subMonitor = SubMonitor.convert(progressMonitor, fileSystemElements.size());
ListIterator<TraceFileSystemElement> fileSystemElementsIter = fileSystemElements.listIterator();
while (fileSystemElementsIter.hasNext()) {
ModalContext.checkCanceled(subMonitor);
SubMonitor elementProgress = subMonitor.newChild(1);
TraceFileSystemElement element = fileSystemElementsIter.next();
elementProgress.setTaskName(Messages.ImportTraceWizard_ExamineOperationTaskName + " " + element.getFileSystemObject().getAbsolutePath()); //$NON-NLS-1$
File archiveFile = (File) element.getFileSystemObject().getRawFileSystemObject();
boolean isArchiveFileElement = element.getFileSystemObject() instanceof FileFileSystemObject && ArchiveUtil.isArchiveFile(archiveFile);
if (isArchiveFileElement) {
elementProgress = SubMonitor.convert(elementProgress, 4);
IPath makeAbsolute = baseSourceContainerPath.makeAbsolute();
IPath relativeToSourceContainer = new Path(element.getFileSystemObject().getAbsolutePath()).makeRelativeTo(makeAbsolute);
IFolder folder = safeCreateExtractedFolder(destFolder, relativeToSourceContainer, elementProgress.newChild(1));
extractArchiveToFolder(archiveFile, folder, elementProgress.newChild(1));
// Delete original archive, we don't want to import this, just
// the extracted content
IFile fileRes = destFolder.getFile(relativeToSourceContainer);
fileRes.delete(true, elementProgress.newChild(1));
IPath newPath = destFolder.getFullPath().append(relativeToSourceContainer);
// Rename extracted folder (.extract) to original archive name
folder.move(newPath, true, elementProgress.newChild(1));
folder = ResourcesPlugin.getWorkspace().getRoot().getFolder(newPath);
elementProgress.subTask(""); //$NON-NLS-1$
// Create the new import provider and root element based on
// the newly extracted temporary folder
FileSystemObjectImportStructureProvider importStructureProvider = new FileSystemObjectImportStructureProvider(FileSystemStructureProvider.INSTANCE, null);
IFileSystemObject rootElement = importStructureProvider.getIFileSystemObject(new File(folder.getLocation().toOSString()));
TraceFileSystemElement newElement = TraceFileSystemElement.createRootTraceFileElement(rootElement, importStructureProvider);
List<TraceFileSystemElement> extractedChildren = new ArrayList<>();
newElement.getAllChildren(extractedChildren);
extractAllArchiveFiles(extractedChildren, folder, folder.getLocation(), progressMonitor);
}
}
}
/**
* Extract a file (File) to a destination folder
*/
private void extractArchiveToFolder(File sourceFile, IFolder destinationFolder, IProgressMonitor progressMonitor) throws InvocationTargetException, InterruptedException {
Pair<IFileSystemObject, FileSystemObjectImportStructureProvider> rootObjectAndProvider = ArchiveUtil.getRootObjectAndProvider(sourceFile, fShell);
TraceFileSystemElement rootElement = TraceFileSystemElement.createRootTraceFileElement(rootObjectAndProvider.getFirst(), rootObjectAndProvider.getSecond());
List<TraceFileSystemElement> fileSystemElements = new ArrayList<>();
rootElement.getAllChildren(fileSystemElements);
extractArchiveContent(fileSystemElements.listIterator(), destinationFolder, progressMonitor);
rootObjectAndProvider.getSecond().dispose();
}
/**
* Safely create a folder meant to receive extracted content by making sure
* there is no name clash.
*/
private static IFolder safeCreateExtractedFolder(IFolder destinationFolder, IPath relativeContainerRelativePath, IProgressMonitor monitor) throws CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, 2);
IFolder extractedFolder;
String suffix = ""; //$NON-NLS-1$
int i = 2;
while (true) {
IPath fullPath = destinationFolder.getFullPath().append(relativeContainerRelativePath + ".extract" + suffix); //$NON-NLS-1$
IFolder folder = ResourcesPlugin.getWorkspace().getRoot().getFolder(fullPath);
if (!folder.exists()) {
extractedFolder = folder;
break;
}
suffix = "(" + i + ")"; //$NON-NLS-1$//$NON-NLS-2$
i++;
}
subMonitor.worked(1);
TraceUtils.createFolder(extractedFolder, subMonitor.newChild(1));
return extractedFolder;
}
private void calculateSourceLocations(List<TraceFileSystemElement> fileSystemElements, String baseSourceLocation) {
for (TraceFileSystemElement element : fileSystemElements) {
IPath tempRelative = new Path(element.getFileSystemObject().getAbsolutePath()).makeRelativeTo(fBaseSourceContainerPath);
String sourceLocation = baseSourceLocation + tempRelative;
element.setSourceLocation(sourceLocation);
TraceFileSystemElement parentElement = (TraceFileSystemElement) element.getParent();
tempRelative = new Path(parentElement.getFileSystemObject().getAbsolutePath()).makeRelativeTo(fBaseSourceContainerPath);
sourceLocation = baseSourceLocation + tempRelative + '/';
parentElement.setSourceLocation(sourceLocation);
}
}
/**
* Extract all file system elements (Tar, Zip elements) to destination
* folder (typically workspace/TraceProject/.traceImport or a subfolder of
* it)
*/
private void extractArchiveContent(Iterator<TraceFileSystemElement> fileSystemElementsIter, IFolder tempFolder, IProgressMonitor progressMonitor) throws InterruptedException,
InvocationTargetException {
List<TraceFileSystemElement> subList = new ArrayList<>();
// Collect all the elements
while (fileSystemElementsIter.hasNext()) {
ModalContext.checkCanceled(progressMonitor);
TraceFileSystemElement element = fileSystemElementsIter.next();
if (element.isDirectory()) {
Object[] array = element.getFiles().getChildren();
for (int i = 0; i < array.length; i++) {
subList.add((TraceFileSystemElement) array[i]);
}
}
subList.add(element);
}
if (subList.isEmpty()) {
return;
}
TraceFileSystemElement root = getRootElement(subList.get(0));
ImportProvider fileSystemStructureProvider = new ImportProvider();
IOverwriteQuery myQueryImpl = file -> IOverwriteQuery.NO_ALL;
progressMonitor.setTaskName(Messages.ImportTraceWizard_ExtractImportOperationTaskName);
IPath containerPath = tempFolder.getFullPath();
ImportOperation operation = new ImportOperation(containerPath, root, fileSystemStructureProvider, myQueryImpl, subList);
operation.setContext(fShell);
operation.setCreateContainerStructure(true);
operation.setOverwriteResources(false);
operation.setVirtualFolders(false);
operation.run(SubMonitor.convert(progressMonitor).newChild(subList.size()));
}
private static TraceFileSystemElement getRootElement(TraceFileSystemElement element) {
TraceFileSystemElement root = element;
while (root.getParent() != null) {
root = (TraceFileSystemElement) root.getParent();
}
return root;
}
private IPath computeDestinationContainerPath(Path resourcePath) {
IPath destinationContainerPath = fDestinationContainerPath;
// We need to figure out the new destination path relative to the
// selected "base" source directory.
// Here for example, the selected source directory is /home/user
if ((fImportOptionFlags & ImportTraceWizardPage.OPTION_PRESERVE_FOLDER_STRUCTURE) != 0) {
// /home/user/bar/foo/trace -> /home/user/bar/foo
IPath sourceContainerPath = resourcePath.removeLastSegments(1);
if (fBaseSourceContainerPath.equals(resourcePath)) {
// Use resourcePath directory if fBaseSourceContainerPath
// points to a directory trace
sourceContainerPath = resourcePath;
}
// /home/user/bar/foo, /home/user -> bar/foo
IPath relativeContainerPath = sourceContainerPath.makeRelativeTo(fBaseSourceContainerPath);
// project/Traces + bar/foo -> project/Traces/bar/foo
destinationContainerPath = fDestinationContainerPath.append(relativeContainerPath);
}
return destinationContainerPath;
}
/**
* Import a single file system element into the workspace.
*/
private void validateAndImportTrace(TraceFileSystemElement fileSystemElement, IProgressMonitor monitor)
throws TmfTraceImportException, CoreException, InvocationTargetException, InterruptedException {
String path = fileSystemElement.getFileSystemObject().getAbsolutePath();
TraceTypeHelper traceTypeHelper = null;
monitor.setTaskName(Messages.ImportTraceWizard_ExamineOperationTaskName + " " + fileSystemElement.getFileSystemObject().getAbsolutePath()); //$NON-NLS-1$
if ((fImportOptionFlags & ImportTraceWizardPage.OPTION_SKIP_ARCHIVE_EXTRACTION) == 0) {
File file = (File) fileSystemElement.getFileSystemObject().getRawFileSystemObject();
boolean isArchiveFileElement = fileSystemElement.getFileSystemObject() instanceof FileFileSystemObject && ArchiveUtil.isArchiveFile(file);
if (isArchiveFileElement) {
// We'll be extracting this later, do not import as a trace
return;
}
}
if (fTraceType == null) {
// Auto Detection
try {
traceTypeHelper = TmfTraceTypeUIUtils.selectTraceType(path, null, null);
} catch (TmfTraceImportException e) {
// the trace did not match any trace type
}
if (traceTypeHelper == null) {
if ((fImportOptionFlags & ImportTraceWizardPage.OPTION_IMPORT_UNRECOGNIZED_TRACES) != 0) {
importResource(fileSystemElement, monitor);
}
return;
}
} else {
boolean isDirectoryTraceType = TmfTraceType.isDirectoryTraceType(fTraceType);
if (fileSystemElement.isDirectory() != isDirectoryTraceType) {
return;
}
traceTypeHelper = TmfTraceType.getTraceType(fTraceType);
if (traceTypeHelper == null) {
// Trace type not found
throw new TmfTraceImportException(Messages.ImportTraceWizard_TraceTypeNotFound);
}
if (!traceTypeHelper.validate(path).isOK()) {
// Trace type exist but doesn't validate for given trace.
return;
}
}
// Finally import trace
TraceFileSystemElement resourceToImport = filterSelectedFileSystemElement(fileSystemElement, monitor);
if(resourceToImport == null) {
return;
}
IResource importedResource = importResource(resourceToImport, monitor);
if (importedResource != null) {
TmfTraceTypeUIUtils.setTraceType(importedResource, traceTypeHelper, false);
fImportedResources.add(importedResource);
}
}
/**
* Imports a trace resource to project. In case of name collision the user
* will be asked to confirm overwriting the existing trace, overwriting or
* skipping the trace to be imported.
*
* @param fileSystemElement
* trace file system object to import
* @param monitor
* a progress monitor
* @return the imported resource or null if no resource was imported
*
* @throws InvocationTargetException
* if problems during import operation
* @throws InterruptedException
* if cancelled
* @throws CoreException
* if problems with workspace
*/
private IResource importResource(TraceFileSystemElement fileSystemElement, IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException, CoreException {
IPath tracePath = getInitialDestinationPath(fileSystemElement);
String newName = fConflictHandler.checkAndHandleNameClash(tracePath, monitor);
if (newName == null) {
return null;
}
fileSystemElement.setLabel(newName);
List<TraceFileSystemElement> subList = new ArrayList<>();
FileSystemElement parentFolder = fileSystemElement.getParent();
IPath containerPath = fileSystemElement.getDestinationContainerPath();
tracePath = containerPath.addTrailingSeparator().append(fileSystemElement.getLabel());
boolean createLinksInWorkspace = (fImportOptionFlags & ImportTraceWizardPage.OPTION_CREATE_LINKS_IN_WORKSPACE) != 0;
if (fileSystemElement.isDirectory() && !createLinksInWorkspace) {
containerPath = tracePath;
Object[] array = fileSystemElement.getFiles().getChildren();
for (int i = 0; i < array.length; i++) {
subList.add((TraceFileSystemElement) array[i]);
}
parentFolder = fileSystemElement;
} else {
if (!fileSystemElement.isDirectory()) {
// File traces
IFileInfo info = EFS.getStore(new File(fileSystemElement.getFileSystemObject().getAbsolutePath()).toURI()).fetchInfo();
if (info.getLength() == 0) {
// Don't import empty traces
return null;
}
}
subList.add(fileSystemElement);
}
ImportProvider fileSystemStructureProvider = new ImportProvider();
IOverwriteQuery myQueryImpl = file -> IOverwriteQuery.NO_ALL;
monitor.setTaskName(Messages.ImportTraceWizard_ImportOperationTaskName + " " + fileSystemElement.getFileSystemObject().getAbsolutePath()); //$NON-NLS-1$
ImportOperation operation = new ImportOperation(containerPath, parentFolder, fileSystemStructureProvider, myQueryImpl, subList);
operation.setContext(fShell);
operation.setCreateContainerStructure(false);
operation.setOverwriteResources(false);
operation.setCreateLinks(createLinksInWorkspace);
operation.setVirtualFolders(false);
operation.run(SubMonitor.convert(monitor).newChild(1));
String sourceLocation = fileSystemElement.getSourceLocation();
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(tracePath);
if ((sourceLocation != null) && (resource != null)) {
resource.setPersistentProperty(TmfCommonConstants.SOURCE_LOCATION, sourceLocation);
}
return resource;
}
private static boolean isDirectoryTrace(TraceFileSystemElement fileSystemElement) {
String path = fileSystemElement.getFileSystemObject().getAbsolutePath();
return (TmfTraceType.isDirectoryTrace(path));
}
/**
* @return the initial destination path, before rename, if any
*/
private static IPath getInitialDestinationPath(TraceFileSystemElement fileSystemElement) {
IPath traceFolderPath = fileSystemElement.getDestinationContainerPath();
return traceFolderPath.append(fileSystemElement.getFileSystemObject().getName());
}
/**
* Set the status for this operation
*
* @param status
* the status
*/
private void setStatus(IStatus status) {
fStatus = status;
}
/**
* Get the resulting status of this operation. Clients can use this for
* error reporting, etc.
*
* @return the resulting status
*/
public IStatus getStatus() {
return fStatus;
}
private class ImportProvider implements IImportStructureProvider {
ImportProvider() {
}
@Override
public String getLabel(Object element) {
TraceFileSystemElement resource = (TraceFileSystemElement) element;
return resource.getLabel();
}
@Override
public List getChildren(Object element) {
TraceFileSystemElement resource = (TraceFileSystemElement) element;
return Arrays.asList(resource.getFiles().getChildren());
}
@Override
public InputStream getContents(Object element) {
TraceFileSystemElement resource = (TraceFileSystemElement) element;
return resource.getProvider().getContents(resource.getFileSystemObject());
}
@Override
public String getFullPath(Object element) {
TraceFileSystemElement resource = (TraceFileSystemElement) element;
return resource.getProvider().getFullPath(resource.getFileSystemObject());
}
@Override
public boolean isFolder(Object element) {
TraceFileSystemElement resource = (TraceFileSystemElement) element;
return resource.isDirectory();
}
}
/**
* Sets the conflict handler
*
* @param conflictHandler
* the conflict handler
*/
public void setConflictHandler(ImportConflictHandler conflictHandler) {
fConflictHandler = conflictHandler;
}
}