1 /*******************************************************************************
2 * Copyright (c) 2013, 2015 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Marc-Andre Laperle - Initial API and implementation
11 * Patrick Tasse - Add support for source location
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.importexport
;
16 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
19 import java
.io
.IOException
;
20 import java
.io
.InputStream
;
21 import java
.lang
.reflect
.InvocationTargetException
;
23 import java
.text
.MessageFormat
;
24 import java
.util
.ArrayList
;
25 import java
.util
.Enumeration
;
26 import java
.util
.List
;
28 import java
.util
.Map
.Entry
;
30 import org
.eclipse
.core
.resources
.IFile
;
31 import org
.eclipse
.core
.resources
.IFolder
;
32 import org
.eclipse
.core
.resources
.IMarker
;
33 import org
.eclipse
.core
.resources
.IResource
;
34 import org
.eclipse
.core
.runtime
.CoreException
;
35 import org
.eclipse
.core
.runtime
.IPath
;
36 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
37 import org
.eclipse
.core
.runtime
.IStatus
;
38 import org
.eclipse
.core
.runtime
.Path
;
39 import org
.eclipse
.core
.runtime
.Status
;
40 import org
.eclipse
.core
.runtime
.SubMonitor
;
41 import org
.eclipse
.core
.runtime
.URIUtil
;
42 import org
.eclipse
.jdt
.annotation
.NonNull
;
43 import org
.eclipse
.jface
.operation
.ModalContext
;
44 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Activator
;
45 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.AbstractTracePackageOperation
;
46 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageBookmarkElement
;
47 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageElement
;
48 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageFilesElement
;
49 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageSupplFileElement
;
50 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageSupplFilesElement
;
51 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageTraceElement
;
52 import org
.eclipse
.tracecompass
.tmf
.core
.TmfCommonConstants
;
53 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceImportException
;
54 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceType
;
55 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TraceTypeHelper
;
56 import org
.eclipse
.tracecompass
.tmf
.core
.util
.Pair
;
57 import org
.eclipse
.tracecompass
.tmf
.ui
.editors
.TmfEventsEditor
;
58 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceElement
;
59 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceFolder
;
60 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceTypeUIUtils
;
61 import org
.eclipse
.ui
.dialogs
.IOverwriteQuery
;
62 import org
.eclipse
.ui
.ide
.IDE
;
63 import org
.eclipse
.ui
.wizards
.datatransfer
.IImportStructureProvider
;
64 import org
.eclipse
.ui
.wizards
.datatransfer
.ImportOperation
;
67 * An operation that imports a trace package from an archive
69 * @author Marc-Andre Laperle
71 public class TracePackageImportOperation
extends AbstractTracePackageOperation
implements IOverwriteQuery
{
73 private final TracePackageElement
[] fImportTraceElements
;
74 private final TmfTraceFolder fTmfTraceFolder
;
77 * Constructs a new import operation
79 * @param importTraceElements
80 * the trace element to be imported
82 * the output file name
83 * @param tmfTraceFolder
84 * the destination folder
86 public TracePackageImportOperation(String fileName
, TracePackageElement
[] importTraceElements
, TmfTraceFolder tmfTraceFolder
) {
88 fImportTraceElements
= importTraceElements
;
89 fTmfTraceFolder
= tmfTraceFolder
;
92 private class ImportProvider
implements IImportStructureProvider
{
94 private Exception fException
;
97 public List
getChildren(Object element
) {
102 public InputStream
getContents(Object element
) {
103 InputStream inputStream
= null;
106 inputStream
= ((ArchiveProviderElement
) element
).getContents();
107 } catch (IOException e
) {
114 public String
getFullPath(Object element
) {
115 return ((ArchiveProviderElement
) element
).getFullPath();
119 public String
getLabel(Object element
) {
120 return ((ArchiveProviderElement
) element
).getLabel();
124 public boolean isFolder(Object element
) {
125 return ((ArchiveProviderElement
) element
).isFolder();
128 public Exception
getException() {
133 private class ArchiveProviderElement
{
135 private final String fPath
;
136 private final String fLabel
;
138 private ArchiveFile fArchiveFile
;
139 private ArchiveEntry fEntry
;
141 public ArchiveProviderElement(String destinationPath
, String label
, ArchiveFile archiveFile
, ArchiveEntry entry
) {
142 fPath
= destinationPath
;
144 this.fArchiveFile
= archiveFile
;
148 public InputStream
getContents() throws IOException
{
149 return fArchiveFile
.getInputStream(fEntry
);
152 public String
getFullPath() {
156 public String
getLabel() {
160 public boolean isFolder() {
166 * Run the operation. The status (result) of the operation can be obtained
167 * with {@link #getStatus}
169 * @param progressMonitor
170 * the progress monitor to use to display progress and receive
171 * requests for cancellation
174 public void run(IProgressMonitor progressMonitor
) {
175 int totalWork
= getNbCheckedElements(fImportTraceElements
) * 2;
176 progressMonitor
.beginTask(Messages
.TracePackageImportOperation_ImportingPackage
, totalWork
);
177 doRun(progressMonitor
);
178 progressMonitor
.done();
181 private void doRun(IProgressMonitor progressMonitor
) {
183 setStatus(deleteExistingTraces(progressMonitor
));
184 if (getStatus().getSeverity() != IStatus
.OK
) {
188 TracePackageFilesElement traceFilesElement
= null;
189 for (TracePackageElement packageElement
: fImportTraceElements
) {
190 TracePackageTraceElement traceElement
= (TracePackageTraceElement
) packageElement
;
191 if (!isFilesChecked(packageElement
)) {
195 TracePackageElement
[] children
= traceElement
.getChildren();
196 for (TracePackageElement element
: children
) {
197 ModalContext
.checkCanceled(progressMonitor
);
199 if (element
instanceof TracePackageFilesElement
) {
200 traceFilesElement
= (TracePackageFilesElement
) element
;
201 setStatus(importTraceFiles(traceFilesElement
, traceElement
, progressMonitor
));
203 } else if (element
instanceof TracePackageSupplFilesElement
) {
204 TracePackageSupplFilesElement suppFilesElement
= (TracePackageSupplFilesElement
) element
;
205 setStatus(importSupplFiles(suppFilesElement
, traceElement
, progressMonitor
));
208 if (getStatus().getSeverity() != IStatus
.OK
) {
214 } catch (InterruptedException e
) {
215 setStatus(Status
.CANCEL_STATUS
);
220 * Returns whether or not the Files element is checked under the given trace
223 * @param tracePackageElement
224 * the trace package element
225 * @return whether or not the Files element is checked under the given trace
228 public static boolean isFilesChecked(TracePackageElement tracePackageElement
) {
229 for (TracePackageElement element
: tracePackageElement
.getChildren()) {
230 if (element
instanceof TracePackageFilesElement
) {
231 return element
.isChecked();
239 * Return the matching TmfTraceElement for a given trace element.
241 private TmfTraceElement
getMatchingTraceElement(TracePackageTraceElement tracePackageElement
) {
242 IPath tracePath
= fTmfTraceFolder
.getPath().append(tracePackageElement
.getDestinationElementPath());
243 List
<TmfTraceElement
> traces
= fTmfTraceFolder
.getTraces();
244 for (TmfTraceElement t
: traces
) {
245 if (t
.getPath().equals(tracePath
)) {
253 private IStatus
deleteExistingTraces(IProgressMonitor progressMonitor
) {
254 for (TracePackageElement packageElement
: fImportTraceElements
) {
255 TracePackageTraceElement traceElement
= (TracePackageTraceElement
) packageElement
;
256 if (!isFilesChecked(traceElement
)) {
260 TmfTraceElement existingTrace
= getMatchingTraceElement(traceElement
);
261 if (existingTrace
!= null) {
263 existingTrace
.delete(SubMonitor
.convert(progressMonitor
));
264 } catch (CoreException e
) {
265 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_ErrorOperation
, e
);
270 return Status
.OK_STATUS
;
273 private void importBookmarks(IResource traceRes
, TracePackageTraceElement traceElement
, IProgressMonitor monitor
) {
274 for (TracePackageElement o
: traceElement
.getChildren()) {
275 if (o
instanceof TracePackageBookmarkElement
&& o
.isChecked()) {
278 IFile bookmarksFile
= null;
279 TmfTraceElement tmfTraceElement
= getMatchingTraceElement(traceElement
);
280 if (tmfTraceElement
!= null) {
282 bookmarksFile
= tmfTraceElement
.createBookmarksFile();
284 // Make sure that if a bookmark is double-clicked first
285 // before opening the trace, it opens the right editor
287 // Get the editor id from the extension point
288 String traceEditorId
= tmfTraceElement
.getEditorId();
289 final String editorId
= (traceEditorId
!= null) ? traceEditorId
: TmfEventsEditor
.ID
;
290 IDE
.setDefaultEditor(bookmarksFile
, editorId
);
292 } catch (CoreException e
) {
293 Activator
.getDefault().logError(MessageFormat
.format(Messages
.TracePackageImportOperation_ErrorCreatingBookmarkFile
, traceRes
.getName()), e
);
297 if (bookmarksFile
== null) {
301 TracePackageBookmarkElement bookmarkElement
= (TracePackageBookmarkElement
) o
;
303 List
<Map
<String
, String
>> bookmarks
= bookmarkElement
.getBookmarks();
304 for (Map
<String
, String
> attrs
: bookmarks
) {
305 IMarker createMarker
= null;
307 createMarker
= bookmarksFile
.createMarker(IMarker
.BOOKMARK
);
308 } catch (CoreException e
) {
309 Activator
.getDefault().logError(MessageFormat
.format(Messages
.TracePackageImportOperation_ErrorCreatingBookmark
, traceRes
.getName()), e
);
311 if (createMarker
!= null && createMarker
.exists()) {
313 for (Entry
<String
, String
> entry
: attrs
.entrySet()) {
314 String key
= entry
.getKey();
315 String value
= entry
.getValue();
316 if (key
.equals(IMarker
.LOCATION
)) {
318 /* try location as an integer for backward compatibility */
319 createMarker
.setAttribute(IMarker
.LOCATION
, Integer
.parseInt(value
));
320 } catch (NumberFormatException e
) {
321 createMarker
.setAttribute(IMarker
.LOCATION
, value
);
324 createMarker
.setAttribute(key
, value
);
327 } catch (CoreException e
) {
328 Activator
.getDefault().logError(MessageFormat
.format(Messages
.TracePackageImportOperation_ErrorCreatingBookmark
, traceRes
.getName()), e
);
338 private IStatus
importTraceFiles(TracePackageFilesElement traceFilesElement
, TracePackageTraceElement traceElement
, IProgressMonitor monitor
) {
339 List
<Pair
<String
, String
>> fileNameAndLabelPairs
= new ArrayList
<>();
341 String sourceName
= checkNotNull(traceFilesElement
.getFileName());
342 String destinationName
= checkNotNull(traceElement
.getImportName());
344 fileNameAndLabelPairs
.add(new Pair
<>(sourceName
, destinationName
));
346 IPath containerPath
= fTmfTraceFolder
.getPath();
347 IStatus status
= importFiles(getSpecifiedArchiveFile(), fileNameAndLabelPairs
, containerPath
, Path
.EMPTY
, monitor
);
348 if (getStatus().getSeverity() != IStatus
.OK
) {
352 // We need to set the trace type before importing the supplementary files so we do it here
353 IResource traceRes
= fTmfTraceFolder
.getResource().findMember(traceElement
.getDestinationElementPath());
354 if (traceRes
== null || !traceRes
.exists()) {
355 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, MessageFormat
.format(Messages
.ImportTracePackageWizardPage_ErrorFindingImportedTrace
, destinationName
));
358 TraceTypeHelper traceType
= null;
359 String traceTypeStr
= traceElement
.getTraceType();
360 if (traceTypeStr
!= null) {
361 traceType
= TmfTraceType
.getTraceType(traceTypeStr
);
362 if (traceType
== null) {
363 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, MessageFormat
.format(Messages
.ImportTracePackageWizardPage_ErrorSettingTraceType
, traceElement
.getTraceType(), destinationName
));
367 monitor
.subTask(MessageFormat
.format(Messages
.TracePackageImportOperation_DetectingTraceType
, destinationName
));
368 traceType
= TmfTraceTypeUIUtils
.selectTraceType(traceRes
.getLocation().toOSString(), null, null);
369 } catch (TmfTraceImportException e
) {
370 // Could not figure out the type
374 if (traceType
!= null) {
376 TmfTraceTypeUIUtils
.setTraceType(traceRes
, traceType
);
377 } catch (CoreException e
) {
378 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, MessageFormat
.format(Messages
.ImportTracePackageWizardPage_ErrorSettingTraceType
, traceElement
.getTraceType(), destinationName
), e
);
382 importBookmarks(traceRes
, traceElement
, monitor
);
385 URI uri
= new File(getFileName()).toURI();
386 IPath entryPath
= new Path(traceFilesElement
.getFileName());
387 if (traceRes
instanceof IFolder
) {
388 entryPath
= entryPath
.addTrailingSeparator();
390 String sourceLocation
= URIUtil
.toUnencodedString(URIUtil
.toJarURI(uri
, entryPath
));
391 traceRes
.setPersistentProperty(TmfCommonConstants
.SOURCE_LOCATION
, sourceLocation
);
392 } catch (CoreException e
) {
398 private IStatus
importSupplFiles(TracePackageSupplFilesElement suppFilesElement
, TracePackageTraceElement traceElement
, IProgressMonitor monitor
) {
399 List
<Pair
<String
, String
>> fileNameAndLabelPairs
= new ArrayList
<>();
400 for (TracePackageElement child
: suppFilesElement
.getChildren()) {
401 if (child
.isChecked()) {
402 TracePackageSupplFileElement supplFile
= (TracePackageSupplFileElement
) child
;
403 fileNameAndLabelPairs
.add(new Pair
<>(checkNotNull(supplFile
.getText()), checkNotNull(new Path(supplFile
.getText()).lastSegment())));
407 if (!fileNameAndLabelPairs
.isEmpty()) {
408 TmfTraceElement existingTrace
= getMatchingTraceElement(traceElement
);
409 if (existingTrace
!= null) {
410 ArchiveFile archiveFile
= getSpecifiedArchiveFile();
411 existingTrace
.refreshSupplementaryFolder();
412 // Project/Traces/A/B -> A/B
413 IPath traceFolderRelativePath
= fTmfTraceFolder
.getPath().makeRelativeTo(fTmfTraceFolder
.getProject().getTracesFolder().getPath());
414 // Project/.tracing/A/B/
415 IFolder traceSupplementaryFolder
= fTmfTraceFolder
.getTraceSupplementaryFolder(traceFolderRelativePath
.toString());
416 IPath destinationContainerPath
= traceSupplementaryFolder
.getFullPath();
417 // Remove the .tracing segment at the beginnin so that a file in folder .tracing/A/B/ imports destinationContainerPath/A/B/
418 Path baseSourcePath
= new Path(TmfCommonConstants
.TRACE_SUPPLEMENTARY_FOLDER_NAME
);
419 return importFiles(archiveFile
, fileNameAndLabelPairs
, destinationContainerPath
, baseSourcePath
, monitor
);
423 return Status
.OK_STATUS
;
426 private IStatus
importFiles(ArchiveFile archiveFile
, List
<Pair
<String
, String
>> fileNameAndLabelPairs
, IPath destinationContainerPath
, IPath baseSourcePath
, IProgressMonitor monitor
) {
427 List
<ArchiveProviderElement
> objects
= new ArrayList
<>();
428 Enumeration
<@NonNull ?
> entries
= archiveFile
.entries();
429 while (entries
.hasMoreElements()) {
430 ArchiveEntry entry
= (ArchiveEntry
) entries
.nextElement();
431 String entryName
= entry
.getName();
432 IPath fullArchivePath
= new Path(entryName
);
433 if (fullArchivePath
.hasTrailingSeparator()) {
434 // We only care about file entries as the folders will get created by the ImportOperation
438 for (Pair
<String
, String
> fileNameAndLabel
: fileNameAndLabelPairs
) {
440 // Examples: Traces/aaa/kernel/ .tracing/aaa/testtexttrace.txt/statistics.ht
441 IPath searchedArchivePath
= new Path(fileNameAndLabel
.getFirst());
443 // Check if this archive entry matches the searched file name at this archive location
444 boolean fileMatch
= entryName
.equalsIgnoreCase(searchedArchivePath
.toString());
445 // For example Traces/aaa/kernel/metadata matches Traces/aaa/kernel/
446 boolean folderMatch
= entryName
.startsWith(searchedArchivePath
+ "/"); //$NON-NLS-1$
448 if (fileMatch
|| folderMatch
) {
449 // .tracing/aaa/testtexttrace.txt/statistics.ht -> aaa/testtexttrace.txt/statistics.ht
450 IPath destinationPath
= fullArchivePath
.makeRelativeTo(baseSourcePath
);
452 // metadata statistics.ht
453 // We don't use the label when the entry is a folder match because the labels for individual files
454 // under the folder are not specified in the manifest so just use the last segment.
455 String resourceLabel
= folderMatch ? fullArchivePath
.lastSegment() : fileNameAndLabel
.getSecond();
457 ArchiveProviderElement pe
= new ArchiveProviderElement(destinationPath
.toString(), resourceLabel
, archiveFile
, entry
);
464 ImportProvider provider
= new ImportProvider();
466 ImportOperation operation
= new ImportOperation(destinationContainerPath
,
467 null, provider
, this,
469 operation
.setCreateContainerStructure(true);
470 operation
.setOverwriteResources(true);
473 operation
.run(SubMonitor
.convert(monitor
));
475 } catch (InvocationTargetException e
) {
476 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_ErrorOperation
, e
);
477 } catch (InterruptedException e
) {
478 return Status
.CANCEL_STATUS
;
479 } catch (IOException e
) {
480 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_ErrorOperation
, e
);
483 if (provider
.getException() != null) {
484 return new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.Messages
.TracePackage_ErrorOperation
, provider
.getException());
487 return operation
.getStatus();
491 public String
queryOverwrite(String pathString
) {
492 // We always overwrite once we reach this point