1 /*******************************************************************************
2 * Copyright (c) 2010, 2015 Ericsson, École Polytechnique de Montréal
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 * Bernd Hufmann - Added supplementary files handling (in class TmfTraceElement)
11 * Geneviève Bastien - Copied supplementary files handling from TmfTracElement
12 * Moved to this class code to copy a model element
13 * Renamed from TmfWithFolderElement to TmfCommonProjectElement
14 * Patrick Tasse - Add support for folder elements
15 *******************************************************************************/
17 package org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
;
19 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
21 import java
.io
.ByteArrayInputStream
;
22 import java
.io
.InputStream
;
23 import java
.util
.ArrayList
;
24 import java
.util
.List
;
25 import java
.util
.stream
.Collectors
;
27 import org
.eclipse
.core
.resources
.IContainer
;
28 import org
.eclipse
.core
.resources
.IFile
;
29 import org
.eclipse
.core
.resources
.IFolder
;
30 import org
.eclipse
.core
.resources
.IResource
;
31 import org
.eclipse
.core
.resources
.IWorkspaceRoot
;
32 import org
.eclipse
.core
.resources
.ResourcesPlugin
;
33 import org
.eclipse
.core
.runtime
.CoreException
;
34 import org
.eclipse
.core
.runtime
.IConfigurationElement
;
35 import org
.eclipse
.core
.runtime
.IPath
;
36 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
37 import org
.eclipse
.core
.runtime
.Path
;
38 import org
.eclipse
.core
.runtime
.Platform
;
39 import org
.eclipse
.jdt
.annotation
.NonNull
;
40 import org
.eclipse
.osgi
.util
.NLS
;
41 import org
.eclipse
.swt
.graphics
.Image
;
42 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.Activator
;
43 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.editors
.ITmfEventsEditorConstants
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.TmfCommonConstants
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceType
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceType
.TraceElementType
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalManager
;
49 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
50 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
51 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
52 import org
.eclipse
.ui
.IEditorReference
;
53 import org
.eclipse
.ui
.IWorkbench
;
54 import org
.eclipse
.ui
.IWorkbenchPage
;
55 import org
.eclipse
.ui
.IWorkbenchWindow
;
56 import org
.eclipse
.ui
.PartInitException
;
57 import org
.eclipse
.ui
.PlatformUI
;
58 import org
.eclipse
.ui
.part
.FileEditorInput
;
59 import org
.osgi
.framework
.Bundle
;
62 * Base class for tracing project elements: it implements the common behavior of
63 * all project elements: supplementary files, analysis, types, etc.
65 * @author Geneviève Bastien
67 public abstract class TmfCommonProjectElement
extends TmfProjectModelElement
{
69 // ------------------------------------------------------------------------
71 // ------------------------------------------------------------------------
73 private static final String BOOKMARKS_HIDDEN_FILE
= ".bookmarks"; //$NON-NLS-1$
75 /* Direct child elements */
76 private TmfViewsElement fViewsElement
= null;
77 private TmfOnDemandAnalysesElement fOnDemandAnalysesElement
= null;
78 private TmfReportsElement fReportsElement
= null;
80 /** This trace type ID as defined in plugin.xml */
81 private String fTraceTypeId
= null;
83 // ------------------------------------------------------------------------
85 // ------------------------------------------------------------------------
88 * Constructor. Creates model element.
91 * The name of the element
97 public TmfCommonProjectElement(String name
, IResource resource
, TmfProjectModelElement parent
) {
98 super(name
, resource
, parent
);
100 TmfSignalManager
.register(this);
103 // ------------------------------------------------------------------------
104 // ITmfProjectModelElement
105 // ------------------------------------------------------------------------
108 public void dispose() {
110 TmfSignalManager
.deregister(this);
117 protected void refreshChildren() {
118 /* Get the base path to put the resource to */
119 IPath tracePath
= getResource().getFullPath();
120 IWorkspaceRoot root
= ResourcesPlugin
.getWorkspace().getRoot();
122 if (fViewsElement
== null) {
123 /* Add the "Views" node */
124 IFolder viewsNodeRes
= root
.getFolder(tracePath
.append(TmfViewsElement
.PATH_ELEMENT
));
125 fViewsElement
= new TmfViewsElement(viewsNodeRes
, this);
126 addChild(fViewsElement
);
128 fViewsElement
.refreshChildren();
130 if (fOnDemandAnalysesElement
== null) {
131 /* Add the "On-demand Analyses" node */
132 IFolder analysesNodeRes
= root
.getFolder(tracePath
.append(TmfOnDemandAnalysesElement
.PATH_ELEMENT
));
133 fOnDemandAnalysesElement
= new TmfOnDemandAnalysesElement(analysesNodeRes
, this);
134 addChild(fOnDemandAnalysesElement
);
136 fOnDemandAnalysesElement
.refreshChildren();
138 if (fReportsElement
== null) {
139 /* Add the "Reports" node */
140 IFolder reportsNodeRes
= root
.getFolder(tracePath
.append(TmfReportsElement
.PATH_ELEMENT
));
141 fReportsElement
= new TmfReportsElement(reportsNodeRes
, this);
142 addChild(fReportsElement
);
144 fReportsElement
.refreshChildren();
151 public Image
getIcon() {
152 String traceType
= getTraceType();
153 if (traceType
== null || TmfTraceType
.getTraceType(traceType
) == null) {
154 // request the label to the Eclipse platform
155 Image icon
= TmfProjectModelIcons
.WORKSPACE_LABEL_PROVIDER
.getImage(getResource());
156 return (icon
== null ? TmfProjectModelIcons
.DEFAULT_TRACE_ICON
: icon
);
159 IConfigurationElement traceUIAttributes
= TmfTraceTypeUIUtils
.getTraceUIAttributes(traceType
,
160 (this instanceof TmfTraceElement
) ? TraceElementType
.TRACE
: TraceElementType
.EXPERIMENT
);
161 if (traceUIAttributes
!= null) {
162 String iconAttr
= traceUIAttributes
.getAttribute(TmfTraceTypeUIUtils
.ICON_ATTR
);
163 if (iconAttr
!= null) {
164 String name
= traceUIAttributes
.getContributor().getName();
166 Bundle bundle
= Platform
.getBundle(name
);
167 if (bundle
!= null) {
168 Image image
= TmfProjectModelIcons
.loadIcon(bundle
, iconAttr
);
176 /* Let subclasses specify an icon */
180 // ------------------------------------------------------------------------
182 // ------------------------------------------------------------------------
185 * Get the child element "Views". There should always be one.
187 * @return The child element
190 protected TmfViewsElement
getChildElementViews() {
191 return fViewsElement
;
195 * Get the child element "Reports".
197 * @return The Reports child element
200 public TmfReportsElement
getChildElementReports() {
201 return fReportsElement
;
205 * Returns the trace type ID.
207 * @return trace type ID.
209 public String
getTraceType() {
214 * Refreshes the trace type field by reading the trace type persistent
215 * property of the resource.
217 public void refreshTraceType() {
219 fTraceTypeId
= TmfTraceType
.getTraceTypeId(getResource());
220 } catch (CoreException e
) {
221 Activator
.getDefault().logError(NLS
.bind(Messages
.TmfCommonProjectElement_ErrorRefreshingProperty
, getName()), e
);
226 * Instantiate a <code>ITmfTrace</code> object based on the trace type and
227 * the corresponding extension.
229 * @return the <code>ITmfTrace</code> or <code>null</code> for an error
231 public abstract ITmfTrace
instantiateTrace();
234 * Return the supplementary folder path for this element. The returned path
235 * is relative to the project's supplementary folder.
237 * @return The supplementary folder path for this element
239 protected String
getSupplementaryFolderPath() {
240 return getElementPath() + getSuffix();
244 * Return the element path relative to its common element (traces folder,
245 * experiments folder or experiment element).
247 * @return The element path
249 public @NonNull String
getElementPath() {
250 ITmfProjectModelElement parent
= getParent();
251 while (!(parent
instanceof TmfTracesFolder
|| parent
instanceof TmfExperimentElement
|| parent
instanceof TmfExperimentFolder
)) {
252 parent
= parent
.getParent();
254 IPath path
= getResource().getFullPath().makeRelativeTo(parent
.getPath());
255 return checkNotNull(path
.toString());
259 * @return The suffix for the supplementary folder
261 protected String
getSuffix() {
262 return ""; //$NON-NLS-1$
266 * Returns a list of TmfTraceElements contained in project element.
268 * @return a list of TmfTraceElements, empty list if none
270 public List
<TmfTraceElement
> getTraces() {
271 return new ArrayList
<>();
275 * Get the instantiated trace associated with this element.
277 * @return The instantiated trace or null if trace is not (yet) available
279 public ITmfTrace
getTrace() {
280 for (ITmfTrace trace
: TmfTraceManager
.getInstance().getOpenedTraces()) {
281 if (trace
.getResource().equals(getResource())) {
289 * Returns the file resource used to store bookmarks after creating it if
290 * necessary. If the trace resource is a file, it is returned directly. If
291 * the trace resource is a folder, a linked file is returned. The file will
292 * be created if it does not exist.
294 * @return the bookmarks file
295 * @throws CoreException
296 * if the bookmarks file cannot be created
298 public abstract IFile
createBookmarksFile() throws CoreException
;
301 * Actually returns the bookmark file or creates it in the project element's
304 * @param bookmarksFolder
305 * Folder where to put the bookmark file
306 * @param editorInputType
307 * The editor input type to set (trace or experiment)
308 * @return The bookmark file
309 * @throws CoreException
310 * if the bookmarks file cannot be created
312 protected IFile
createBookmarksFile(IFolder bookmarksFolder
, String editorInputType
) throws CoreException
{
313 IFile file
= getBookmarksFile();
314 if (!file
.exists()) {
315 final IFile bookmarksFile
= bookmarksFolder
.getFile(BOOKMARKS_HIDDEN_FILE
);
316 if (!bookmarksFile
.exists()) {
317 final InputStream source
= new ByteArrayInputStream(new byte[0]);
318 bookmarksFile
.create(source
, IResource
.FORCE
| IResource
.HIDDEN
, null);
320 file
.createLink(bookmarksFile
.getLocation(), IResource
.REPLACE
| IResource
.HIDDEN
, null);
321 file
.setPersistentProperty(TmfCommonConstants
.TRACETYPE
, editorInputType
);
327 * Returns the optional editor ID from the trace type extension.
329 * @return the editor ID or <code>null</code> if not defined.
331 public abstract String
getEditorId();
334 * Returns the file resource used to store bookmarks. The file may not
337 * @return the bookmarks file
339 public IFile
getBookmarksFile() {
340 final IFolder folder
= (IFolder
) getResource();
341 IFile file
= folder
.getFile(getName() + '_');
346 * Close open editors associated with this experiment.
348 public void closeEditors() {
349 IFile file
= getBookmarksFile();
350 FileEditorInput input
= new FileEditorInput(file
);
351 IWorkbench wb
= PlatformUI
.getWorkbench();
352 for (IWorkbenchWindow wbWindow
: wb
.getWorkbenchWindows()) {
353 for (IWorkbenchPage wbPage
: wbWindow
.getPages()) {
354 for (IEditorReference editorReference
: wbPage
.getEditorReferences()) {
356 if (editorReference
.getEditorInput().equals(input
)) {
357 wbPage
.closeEditor(editorReference
.getEditor(false), false);
359 } catch (PartInitException e
) {
360 Activator
.getDefault().logError(NLS
.bind(Messages
.TmfCommonProjectElement_ErrorClosingEditor
, getName()), e
);
368 * Get a friendly name for the type of element this common project element
369 * is, to be displayed in UI messages.
371 * @return A string for the type of project element this object is, for
372 * example "trace" or "experiment"
374 public abstract String
getTypeName();
377 * Copy this model element
380 * The name of the new element
381 * @param copySuppFiles
382 * Whether to copy supplementary files or not
383 * @return the new Resource object
385 public IResource
copy(final String newName
, final boolean copySuppFiles
) {
387 final IPath newPath
= getParent().getResource().getFullPath().addTrailingSeparator().append(newName
);
389 /* Copy supplementary files first, only if needed */
391 String newElementPath
= new Path(getElementPath()).removeLastSegments(1).append(newName
).toString();
392 copySupplementaryFolder(newElementPath
);
396 getResource().copy(newPath
, IResource
.FORCE
| IResource
.SHALLOW
, null);
397 IResource trace
= ((IFolder
) getParent().getResource()).findMember(newName
);
399 /* Delete any bookmarks file found in copied trace folder */
400 if (trace
instanceof IFolder
) {
401 IFolder folderTrace
= (IFolder
) trace
;
402 for (IResource member
: folderTrace
.members()) {
403 String traceTypeId
= TmfTraceType
.getTraceTypeId(member
);
404 if (ITmfEventsEditorConstants
.TRACE_INPUT_TYPE_CONSTANTS
.contains(traceTypeId
)) {
405 member
.delete(true, null);
406 } else if (ITmfEventsEditorConstants
.EXPERIMENT_INPUT_TYPE_CONSTANTS
.contains(traceTypeId
)) {
407 member
.delete(true, null);
412 } catch (CoreException e
) {
419 * Get the list of analysis elements
421 * @return Array of analysis elements
423 public List
<TmfAnalysisElement
> getAvailableAnalysis() {
424 return getChildElementViews().getChildren().stream()
425 .map(elem
-> (TmfAnalysisElement
) elem
)
426 .collect(Collectors
.toList());
429 // ------------------------------------------------------------------------
430 // Supplementary files operations
431 // ------------------------------------------------------------------------
434 * Deletes this element specific supplementary folder.
436 public void deleteSupplementaryFolder() {
437 IFolder supplFolder
= getTraceSupplementaryFolder(getSupplementaryFolderPath());
439 deleteFolder(supplFolder
);
440 } catch (CoreException e
) {
441 Activator
.getDefault().logError("Error deleting supplementary folder " + supplFolder
, e
); //$NON-NLS-1$
445 private static void deleteFolder(IFolder folder
) throws CoreException
{
446 if (folder
.exists()) {
447 folder
.delete(true, new NullProgressMonitor());
449 IContainer parent
= folder
.getParent();
450 // delete empty folders up to the parent project
451 if (parent
instanceof IFolder
&& (!parent
.exists() || parent
.members().length
== 0)) {
452 deleteFolder((IFolder
) parent
);
457 * Renames the element specific supplementary folder according to the new
458 * element name or path.
460 * @param newElementPath
461 * The new element name or path
463 public void renameSupplementaryFolder(String newElementPath
) {
464 IFolder oldSupplFolder
= getTraceSupplementaryFolder(getSupplementaryFolderPath());
466 // Rename supplementary folder
468 if (oldSupplFolder
.exists()) {
469 IFolder newSupplFolder
= prepareTraceSupplementaryFolder(newElementPath
+ getSuffix(), false);
470 oldSupplFolder
.move(newSupplFolder
.getFullPath(), true, new NullProgressMonitor());
472 deleteFolder(oldSupplFolder
);
473 } catch (CoreException e
) {
474 Activator
.getDefault().logError("Error renaming supplementary folder " + oldSupplFolder
, e
); //$NON-NLS-1$
479 * Copies the element specific supplementary folder to the new element name
482 * @param newElementPath
483 * The new element name or path
485 public void copySupplementaryFolder(String newElementPath
) {
486 IFolder oldSupplFolder
= getTraceSupplementaryFolder(getSupplementaryFolderPath());
488 // copy supplementary folder
489 if (oldSupplFolder
.exists()) {
491 IFolder newSupplFolder
= prepareTraceSupplementaryFolder(newElementPath
+ getSuffix(), false);
492 oldSupplFolder
.copy(newSupplFolder
.getFullPath(), true, new NullProgressMonitor());
493 } catch (CoreException e
) {
494 Activator
.getDefault().logError("Error renaming supplementary folder " + oldSupplFolder
, e
); //$NON-NLS-1$
500 * Copies the element specific supplementary folder a new folder.
503 * The destination folder to copy to.
505 public void copySupplementaryFolder(IFolder destination
) {
506 IFolder oldSupplFolder
= getTraceSupplementaryFolder(getSupplementaryFolderPath());
508 // copy supplementary folder
509 if (oldSupplFolder
.exists()) {
511 TraceUtils
.createFolder((IFolder
) destination
.getParent(), new NullProgressMonitor());
512 oldSupplFolder
.copy(destination
.getFullPath(), true, new NullProgressMonitor());
513 } catch (CoreException e
) {
514 Activator
.getDefault().logError("Error copying supplementary folder " + oldSupplFolder
, e
); //$NON-NLS-1$
520 * Refreshes the element specific supplementary folder information. It
521 * creates the folder if not exists. It sets the persistence property of the
524 public void refreshSupplementaryFolder() {
525 IFolder supplFolder
= createSupplementaryFolder();
527 supplFolder
.refreshLocal(IResource
.DEPTH_INFINITE
, new NullProgressMonitor());
528 } catch (CoreException e
) {
529 Activator
.getDefault().logError("Error refreshing supplementary folder " + supplFolder
, e
); //$NON-NLS-1$
534 * Checks if supplementary resource exist or not.
536 * @return <code>true</code> if one or more files are under the element
537 * supplementary folder
539 public boolean hasSupplementaryResources() {
540 IResource
[] resources
= getSupplementaryResources();
541 return (resources
.length
> 0);
545 * Returns the supplementary resources under the trace supplementary folder.
547 * @return array of resources under the trace supplementary folder.
549 public IResource
[] getSupplementaryResources() {
550 IFolder supplFolder
= getTraceSupplementaryFolder(getSupplementaryFolderPath());
551 if (supplFolder
.exists()) {
553 return supplFolder
.members();
554 } catch (CoreException e
) {
555 Activator
.getDefault().logError("Error deleting supplementary folder " + supplFolder
, e
); //$NON-NLS-1$
558 return new IResource
[0];
562 * Deletes the given resources.
565 * array of resources to delete.
567 public void deleteSupplementaryResources(IResource
[] resources
) {
569 for (int i
= 0; i
< resources
.length
; i
++) {
571 resources
[i
].delete(true, new NullProgressMonitor());
572 } catch (CoreException e
) {
573 Activator
.getDefault().logError("Error deleting supplementary resource " + resources
[i
], e
); //$NON-NLS-1$
579 * Deletes all supplementary resources in the supplementary directory
581 public void deleteSupplementaryResources() {
582 deleteSupplementaryResources(getSupplementaryResources());
585 private IFolder
createSupplementaryFolder() {
586 IFolder supplFolder
= prepareTraceSupplementaryFolder(getSupplementaryFolderPath(), true);
589 getResource().setPersistentProperty(TmfCommonConstants
.TRACE_SUPPLEMENTARY_FOLDER
, supplFolder
.getLocation().toOSString());
590 } catch (CoreException e
) {
591 Activator
.getDefault().logError("Error setting persistant property " + TmfCommonConstants
.TRACE_SUPPLEMENTARY_FOLDER
, e
); //$NON-NLS-1$
596 // -------------------------------------------------------
598 // -------------------------------------------------------
601 * Handler for the Trace Opened signal
604 * The incoming signal
607 public void traceOpened(TmfTraceOpenedSignal signal
) {
608 IResource resource
= signal
.getTrace().getResource();
609 if ((resource
== null) || !resource
.equals(getResource())) {
613 getParent().refresh();