blob: fe603e993afe7c8308f187e504c1754010fbb60d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2018 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.tracepkg.importexport;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.tracecompass.common.core.xml.XmlUtils;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.AbstractTracePackageOperation;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.ITracePackageConstants;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageBookmarkElement;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageElement;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageExperimentElement;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageFilesElement;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageSupplFileElement;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageSupplFilesElement;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageTraceElement;
import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfCommonProjectElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfExperimentElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfProjectModelElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceElement;
import org.eclipse.tracecompass.tmf.ui.project.model.TraceUtils;
import org.eclipse.ui.internal.wizards.datatransfer.ArchiveFileExportOperation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* An operation that exports a trace package to an archive
*
* @author Marc-Andre Laperle
*/
@SuppressWarnings("restriction")
public class TracePackageExportOperation extends AbstractTracePackageOperation {
private static final String TRACE_EXPORT_TEMP_FOLDER = ".traceExport"; //$NON-NLS-1$
private final TracePackageTraceElement[] fTraceExportElements;
private final boolean fUseCompression;
private final boolean fUseTar;
private final Set<IResource> fResources;
private IFolder fExportFolder;
/**
* Constructs a new export operation
*
* @param traceExportElements
* the trace elements to be exported
* @param useCompression
* whether or not to use compression
* @param useTar
* use tar format or zip
* @param fileName
* the output file name
*/
public TracePackageExportOperation(TracePackageTraceElement[] traceExportElements, boolean useCompression, boolean useTar, String fileName) {
super(fileName);
fTraceExportElements = traceExportElements;
fUseCompression = useCompression;
fUseTar = useTar;
fResources = new HashSet<>();
}
/**
* Run the operation. The status (result) of the operation can be obtained
* with {@link #getStatus}
*
* @param progressMonitor
* the progress monitor to use to display progress and receive
* requests for cancellation
*/
@Override
public void run(IProgressMonitor progressMonitor) {
try {
int totalWork = getNbCheckedElements(fTraceExportElements) * 2;
progressMonitor.beginTask(Messages.TracePackageExportOperation_GeneratingPackage, totalWork);
fExportFolder = createExportFolder(progressMonitor);
Document doc = XmlUtils.newSafeDocumentBuilderFactory().newDocumentBuilder().newDocument();
Element createElement = doc.createElement(ITracePackageConstants.TMF_EXPORT_ELEMENT);
Node tmfNode = doc.appendChild(createElement);
List<TracePackageExperimentElement> experimentPackageElements = new ArrayList<>();
for (TracePackageTraceElement tracePackageElement : fTraceExportElements) {
if (!isFilesChecked(tracePackageElement)) {
continue;
}
if (tracePackageElement instanceof TracePackageExperimentElement) {
experimentPackageElements.add((TracePackageExperimentElement) tracePackageElement);
} else {
exportTrace(progressMonitor, tmfNode, tracePackageElement);
}
}
for (TracePackageExperimentElement experimentPackageElement : experimentPackageElements) {
exportExperiment(progressMonitor, tmfNode, experimentPackageElement);
}
Transformer transformer = XmlUtils.newSecureTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); //$NON-NLS-1$ //$NON-NLS-2$
DOMSource source = new DOMSource(doc);
StringWriter buffer = new StringWriter();
StreamResult result = new StreamResult(buffer);
transformer.transform(source, result);
String content = buffer.getBuffer().toString();
ModalContext.checkCanceled(progressMonitor);
exportManifest(content);
setStatus(exportToArchive(progressMonitor, totalWork));
fExportFolder.delete(true, SubMonitor.convert(progressMonitor));
progressMonitor.done();
} catch (InterruptedException e) {
setStatus(Status.CANCEL_STATUS);
Thread.currentThread().interrupt();
} catch (Exception e) {
setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_ErrorOperation, e));
}
}
private IFolder createExportFolder(IProgressMonitor monitor) throws CoreException {
IFolder folder = fTraceExportElements[0].getTraceElement().getProject().getResource().getFolder(TRACE_EXPORT_TEMP_FOLDER);
if (folder.exists()) {
folder.delete(true, null);
}
folder.create(IResource.FORCE | IResource.HIDDEN, true, SubMonitor.convert(monitor));
return folder;
}
private void exportTrace(IProgressMonitor monitor, Node tmfNode, TracePackageTraceElement tracePackageElement) throws InterruptedException, CoreException {
exportCommon(monitor, tmfNode, tracePackageElement, ITracePackageConstants.TRACE_ELEMENT);
}
private void exportExperiment(IProgressMonitor monitor, Node tmfNode, TracePackageExperimentElement experimentPackageElement) throws InterruptedException, CoreException {
Node expNode = exportCommon(monitor, tmfNode, experimentPackageElement, ITracePackageConstants.EXPERIMENT_ELEMENT);
TmfExperimentElement experimentElement = (TmfExperimentElement) experimentPackageElement.getTraceElement();
for (TmfTraceElement traceElement : experimentElement.getTraces()) {
Element expTraceXmlElement = expNode.getOwnerDocument().createElement(ITracePackageConstants.EXP_TRACE_ELEMENT);
expTraceXmlElement.setAttribute(ITracePackageConstants.TRACE_NAME_ATTRIB, traceElement.getElementPath());
expNode.appendChild(expTraceXmlElement);
}
}
private Node exportCommon(IProgressMonitor monitor, Node tmfNode, TracePackageTraceElement tracePackageElement, String elementString) throws InterruptedException, CoreException {
TmfCommonProjectElement commonElement = tracePackageElement.getTraceElement();
Element commonXmlElement = tmfNode.getOwnerDocument().createElement(elementString);
commonXmlElement.setAttribute(ITracePackageConstants.TRACE_NAME_ATTRIB, commonElement.getResource().getName());
commonXmlElement.setAttribute(ITracePackageConstants.TRACE_TYPE_ATTRIB, commonElement.getTraceType());
Node commonNode = tmfNode.appendChild(commonXmlElement);
for (TracePackageElement element : tracePackageElement.getChildren()) {
ModalContext.checkCanceled(monitor);
if (!element.isChecked()) {
continue;
}
if (element instanceof TracePackageSupplFilesElement) {
exportSupplementaryFiles(monitor, commonNode, commonElement, (TracePackageSupplFilesElement) element);
} else if (element instanceof TracePackageBookmarkElement) {
exportBookmarks(monitor, commonNode, (TracePackageBookmarkElement) element);
} else if (element instanceof TracePackageFilesElement && commonElement instanceof TmfTraceElement) {
exportTraceFiles(monitor, commonNode, (TracePackageFilesElement) element);
}
monitor.worked(1);
}
return commonNode;
}
private void exportSupplementaryFiles(IProgressMonitor monitor, Node commonNode, TmfCommonProjectElement commonElement, TracePackageSupplFilesElement element) throws InterruptedException, CoreException {
Document doc = commonNode.getOwnerDocument();
if (element.getChildren().length > 0) {
IPath projectPath = commonElement.getProject().getPath();
for (TracePackageElement child : element.getChildren()) {
TracePackageSupplFileElement supplFile = (TracePackageSupplFileElement) child;
ModalContext.checkCanceled(monitor);
IResource res = supplFile.getResource();
// project/.tracing/A/B/statistics.ht -> .tracing/A/B/statistics.ht
IPath relativeToExportFolder = res.getFullPath().makeRelativeTo(projectPath);
// project/.traceExport/.tracing/A/B
IFolder folder = fExportFolder.getFolder(relativeToExportFolder.removeLastSegments(1));
TraceUtils.createFolder(folder, SubMonitor.convert(monitor));
res.refreshLocal(0, SubMonitor.convert(monitor));
createExportResource(folder, res);
Element suppFileElement = doc.createElement(ITracePackageConstants.SUPPLEMENTARY_FILE_ELEMENT);
suppFileElement.setAttribute(ITracePackageConstants.SUPPLEMENTARY_FILE_NAME_ATTRIB, relativeToExportFolder.toString());
commonNode.appendChild(suppFileElement);
}
IFolder suppFilesFolder = fExportFolder.getFolder(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER_NAME);
fResources.add(suppFilesFolder);
}
}
private void exportTraceFiles(IProgressMonitor monitor, Node traceNode, TracePackageFilesElement element) throws CoreException {
Document doc = traceNode.getOwnerDocument();
TmfCommonProjectElement traceElement = ((TracePackageTraceElement) element.getParent()).getTraceElement();
IResource resource = traceElement.getResource();
final TmfProjectModelElement tracesFolder = traceElement.getProject().getTracesFolder();
if (tracesFolder == null) {
return;
}
IPath traceFolderPath = tracesFolder.getPath();
// project/Traces/A/B/Kernel -> A/B/Kernel
IPath relativeToExportFolder = resource.getFullPath().makeRelativeTo(traceFolderPath);
// project/.traceExport/A/B
IFolder folder = fExportFolder.getFolder(relativeToExportFolder.removeLastSegments(1));
TraceUtils.createFolder(folder, SubMonitor.convert(monitor));
createExportResource(folder, resource);
Element fileElement = doc.createElement(ITracePackageConstants.TRACE_FILE_ELEMENT);
fileElement.setAttribute(ITracePackageConstants.TRACE_FILE_NAME_ATTRIB, relativeToExportFolder.toString());
traceNode.appendChild(fileElement);
// Always export the top-most folder containing the trace or the
// trace itself
IResource exportedResource = fExportFolder.findMember(relativeToExportFolder.segment(0));
fResources.add(exportedResource);
}
/**
* Creates a linked resource in the specified folder
*
* @param exportFolder the folder that will contain the linked resource
* @param res the resource to export
* @throws CoreException when createLink fails
* @return the created linked resource
*/
private static IResource createExportResource(IFolder exportFolder, IResource res) throws CoreException {
IResource ret = null;
// Note: The resources cannot be HIDDEN or else they are ignored by ArchiveFileExportOperation
if (res instanceof IFolder) {
IFolder folder = exportFolder.getFolder(res.getName());
folder.createLink(res.getLocationURI(), IResource.REPLACE, null);
ret = folder;
} else if (res instanceof IFile) {
IFile file = exportFolder.getFile(res.getName());
if (!file.exists()) {
file.createLink(res.getLocationURI(), IResource.NONE, null);
}
ret = file;
}
return ret;
}
private static void exportBookmarks(IProgressMonitor monitor, Node commonNode, TracePackageBookmarkElement element) throws CoreException, InterruptedException {
Document doc = commonNode.getOwnerDocument();
IFile bookmarksFile = ((TracePackageTraceElement) element.getParent()).getTraceElement().getBookmarksFile();
if (bookmarksFile != null && bookmarksFile.exists()) {
IMarker[] findMarkers = bookmarksFile.findMarkers(IMarker.BOOKMARK, false, IResource.DEPTH_ZERO);
if (findMarkers.length > 0) {
Element bookmarksXmlElement = doc.createElement(ITracePackageConstants.BOOKMARKS_ELEMENT);
Node bookmarksNode = commonNode.appendChild(bookmarksXmlElement);
for (IMarker marker : findMarkers) {
ModalContext.checkCanceled(monitor);
Element singleBookmarkXmlElement = doc.createElement(ITracePackageConstants.BOOKMARK_ELEMENT);
for (String key : marker.getAttributes().keySet()) {
singleBookmarkXmlElement.setAttribute(key, marker.getAttribute(key).toString());
}
bookmarksNode.appendChild(singleBookmarkXmlElement);
}
}
}
}
private void exportManifest(String content) throws CoreException {
IFile file = fExportFolder.getFile(ITracePackageConstants.MANIFEST_FILENAME);
ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes());
if (file.exists()) {
file.setContents(inputStream, IResource.FORCE, null);
} else {
file.create(inputStream, IResource.FORCE | IResource.HIDDEN, null);
}
fResources.add(file);
}
private IStatus exportToArchive(IProgressMonitor monitor, int totalWork) throws InvocationTargetException, InterruptedException {
ArchiveFileExportOperation op = new ArchiveFileExportOperation(new ArrayList<>(fResources), getFileName());
op.setCreateLeadupStructure(false);
op.setIncludeLinkedResources(true);
op.setUseCompression(fUseCompression);
op.setUseTarFormat(fUseTar);
op.run(SubMonitor.convert(monitor).newChild(totalWork / 2));
return op.getStatus();
}
}