| /******************************************************************************* |
| * 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(); |
| } |
| } |