1 /*******************************************************************************
2 * Copyright (c) 2014, 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 * Alexandre Montplaisir - Initial API and implementation
11 * Patrick Tasse - Add support for folder elements
12 * Bernd Hufmann - Update trace type auto-detection
13 *******************************************************************************/
15 package org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
;
18 import java
.lang
.reflect
.Constructor
;
19 import java
.lang
.reflect
.InvocationTargetException
;
20 import java
.util
.ArrayList
;
21 import java
.util
.Comparator
;
22 import java
.util
.HashMap
;
23 import java
.util
.LinkedList
;
24 import java
.util
.List
;
26 import java
.util
.TreeSet
;
28 import org
.eclipse
.core
.resources
.IResource
;
29 import org
.eclipse
.core
.runtime
.CoreException
;
30 import org
.eclipse
.core
.runtime
.IConfigurationElement
;
31 import org
.eclipse
.core
.runtime
.IStatus
;
32 import org
.eclipse
.core
.runtime
.Platform
;
33 import org
.eclipse
.core
.runtime
.Status
;
34 import org
.eclipse
.jdt
.annotation
.Nullable
;
35 import org
.eclipse
.osgi
.util
.NLS
;
36 import org
.eclipse
.swt
.SWT
;
37 import org
.eclipse
.swt
.events
.SelectionEvent
;
38 import org
.eclipse
.swt
.events
.SelectionListener
;
39 import org
.eclipse
.swt
.layout
.RowLayout
;
40 import org
.eclipse
.swt
.widgets
.Button
;
41 import org
.eclipse
.swt
.widgets
.Composite
;
42 import org
.eclipse
.swt
.widgets
.Display
;
43 import org
.eclipse
.swt
.widgets
.Shell
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.TmfCommonConstants
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.parsers
.custom
.CustomTxtTrace
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.parsers
.custom
.CustomXmlTrace
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceImportException
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceType
;
49 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceType
.TraceElementType
;
50 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TraceTypeHelper
;
51 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
52 import org
.eclipse
.tracecompass
.tmf
.core
.util
.Pair
;
53 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.events
.TmfEventsTable
;
54 import org
.osgi
.framework
.Bundle
;
57 * Utils class for the UI-specific parts of @link {@link TmfTraceType}.
59 * @author Alexandre Montplaisir
61 public final class TmfTraceTypeUIUtils
{
63 /** Extension point ID */
64 public static final String TMF_TRACE_TYPE_UI_ID
= "org.eclipse.linuxtools.tmf.ui.tracetypeui"; //$NON-NLS-1$
66 /** Extension point element 'type' (should match the type in TmfTraceType) */
67 public static final String TYPE_ELEM
= "type"; //$NON-NLS-1$
70 * Extension point element 'experiment' (should match the type in
73 public static final String EXPERIMENT_ELEM
= "experiment"; //$NON-NLS-1$
75 /** Extension point element 'Default editor' */
76 public static final String DEFAULT_EDITOR_ELEM
= "defaultEditor"; //$NON-NLS-1$
78 /** Extension point element 'Events table type' */
79 public static final String EVENTS_TABLE_TYPE_ELEM
= "eventsTableType"; //$NON-NLS-1$
81 /** Extension point element 'Event Table Columns' */
82 public static final String EVENT_TABLE_COLUMNS
= "eventTableColumns"; //$NON-NLS-1$
84 /** Extension point attribute 'tracetype' */
85 public static final String TRACETYPE_ATTR
= "tracetype"; //$NON-NLS-1$
87 /** Extension point attribute 'icon' */
88 public static final String ICON_ATTR
= "icon"; //$NON-NLS-1$
90 /** Extension point attribute 'class' (attribute of other elements) */
91 public static final String CLASS_ATTR
= "class"; //$NON-NLS-1$
93 private TmfTraceTypeUIUtils() {
96 private static List
<Pair
<Integer
, TraceTypeHelper
>> reduce(List
<Pair
<Integer
, TraceTypeHelper
>> candidates
) {
97 List
<Pair
<Integer
, TraceTypeHelper
>> retVal
= new ArrayList
<>();
99 // get all the tracetypes that are unique in that stage
100 for (Pair
<Integer
, TraceTypeHelper
> candidatePair
: candidates
) {
101 TraceTypeHelper candidate
= candidatePair
.getSecond();
102 if (isUnique(candidate
, candidates
)) {
103 retVal
.add(candidatePair
);
110 * Only return the leaves of the trace types. Ignore custom trace types.
112 private static boolean isUnique(TraceTypeHelper trace
, List
<Pair
<Integer
, TraceTypeHelper
>> set
) {
113 if (trace
.getTraceClass().equals(CustomTxtTrace
.class) ||
114 trace
.getTraceClass().equals(CustomXmlTrace
.class)) {
117 // check if the trace type is the leaf. we make an instance of the trace
118 // type and if it is only an instance of itself, it is a leaf
119 final ITmfTrace tmfTrace
= trace
.getTrace();
121 for (Pair
<Integer
, TraceTypeHelper
> child
: set
) {
122 final ITmfTrace traceCandidate
= child
.getSecond().getTrace();
123 if (tmfTrace
.getClass().isInstance(traceCandidate
)) {
130 private static TraceTypeHelper
getTraceTypeToSet(List
<Pair
<Integer
, TraceTypeHelper
>> candidates
, Shell shell
) {
131 final Map
<String
, String
> names
= new HashMap
<>();
132 Shell shellToShow
= new Shell(shell
);
133 shellToShow
.setText(Messages
.TmfTraceType_SelectTraceType
);
134 final String candidatesToSet
[] = new String
[1];
135 for (Pair
<Integer
, TraceTypeHelper
> candidatePair
: candidates
) {
136 TraceTypeHelper candidate
= candidatePair
.getSecond();
137 Button b
= new Button(shellToShow
, SWT
.RADIO
);
138 final String displayName
= candidate
.getCategoryName() + ':' + candidate
.getName();
139 b
.setText(displayName
);
140 names
.put(displayName
, candidate
.getTraceTypeId());
142 b
.addSelectionListener(new SelectionListener() {
145 public void widgetSelected(SelectionEvent e
) {
146 final Button source
= (Button
) e
.getSource();
147 candidatesToSet
[0] = (names
.get(source
.getText()));
148 source
.getParent().dispose();
152 public void widgetDefaultSelected(SelectionEvent e
) {
157 shellToShow
.setLayout(new RowLayout(SWT
.VERTICAL
));
161 Display display
= shellToShow
.getDisplay();
162 while (!shellToShow
.isDisposed()) {
163 if (!display
.readAndDispatch()) {
167 return TmfTraceType
.getTraceType(candidatesToSet
[0]);
171 * This member figures out the trace type of a given file. It will prompt
172 * the user if it needs more information to properly pick the trace type.
175 * The path of file to import
177 * a shell to display the message to. If it is null, it is
178 * assumed to be cancelled.
179 * @param traceTypeHint
180 * the ID of a trace (like "o.e.l.specifictrace" )
181 * @return null if the request is cancelled or a TraceTypeHelper if it
183 * @throws TmfTraceImportException
184 * if the traces don't match or there are errors in the trace
187 public static TraceTypeHelper
selectTraceType(String path
, Shell shell
, String traceTypeHint
) throws TmfTraceImportException
{
189 Comparator
<Pair
<Integer
, TraceTypeHelper
>> comparator
= new Comparator
<Pair
<Integer
, TraceTypeHelper
>>() {
191 public int compare(Pair
<Integer
, TraceTypeHelper
> o1
, Pair
<Integer
, TraceTypeHelper
> o2
) {
192 int res
= -o1
.getFirst().compareTo(o2
.getFirst()); // invert so that highest confidence is first
194 res
= o1
.getSecond().getName().compareTo(o2
.getSecond().getName());
199 TreeSet
<Pair
<Integer
, TraceTypeHelper
>> validCandidates
= new TreeSet
<>(comparator
);
200 final Iterable
<TraceTypeHelper
> traceTypeHelpers
= TmfTraceType
.getTraceTypeHelpers();
201 for (TraceTypeHelper traceTypeHelper
: traceTypeHelpers
) {
202 if (traceTypeHelper
.isExperimentType()) {
205 int confidence
= traceTypeHelper
.validateWithConfidence(path
);
206 if (confidence
>= 0) {
207 // insert in the tree map, ordered by confidence (highest confidence first) then name
208 Pair
<Integer
, TraceTypeHelper
> element
= new Pair
<>(confidence
, traceTypeHelper
);
209 validCandidates
.add(element
);
213 TraceTypeHelper traceTypeToSet
= null;
214 if (validCandidates
.isEmpty()) {
215 File traceFile
= new File(path
);
216 if (traceFile
.isFile()) {
219 final String errorMsg
= NLS
.bind(Messages
.TmfOpenTraceHelper_NoTraceTypeMatch
, path
);
220 throw new TmfTraceImportException(errorMsg
);
223 if (validCandidates
.size() != 1) {
224 List
<Pair
<Integer
, TraceTypeHelper
>> candidates
= new ArrayList
<>(validCandidates
);
225 List
<Pair
<Integer
, TraceTypeHelper
>> reducedCandidates
= reduce(candidates
);
226 for (Pair
<Integer
, TraceTypeHelper
> candidatePair
: reducedCandidates
) {
227 TraceTypeHelper candidate
= candidatePair
.getSecond();
228 if (candidate
.getTraceTypeId().equals(traceTypeHint
)) {
229 traceTypeToSet
= candidate
;
233 if (traceTypeToSet
== null) {
234 if (reducedCandidates
.size() == 0) {
235 throw new TmfTraceImportException(Messages
.TmfOpenTraceHelper_ReduceError
);
236 } else if (reducedCandidates
.size() == 1) {
237 // Don't select the trace type if it has the lowest confidence
238 if (reducedCandidates
.get(0).getFirst() > 0) {
239 traceTypeToSet
= reducedCandidates
.get(0).getSecond();
241 } else if (shell
== null) {
242 Pair
<Integer
, TraceTypeHelper
> candidate
= reducedCandidates
.get(0);
243 // if the best match has lowest confidence, don't select it
244 if (candidate
.getFirst() > 0) {
245 traceTypeToSet
= candidate
.getSecond();
248 traceTypeToSet
= getTraceTypeToSet(reducedCandidates
, shell
);
252 // Don't select the trace type if it has the lowest confidence
253 if (validCandidates
.first().getFirst() > 0) {
254 traceTypeToSet
= validCandidates
.first().getSecond();
257 return traceTypeToSet
;
261 * Set the trace type of a {@link TraceTypeHelper}. Should only be
262 * used internally by this project.
265 * the resource to set
267 * the {@link TraceTypeHelper} to set the trace type to.
268 * @return Status.OK_Status if successful, error is otherwise.
269 * @throws CoreException
270 * An exception caused by accessing eclipse project items.
272 public static IStatus
setTraceType(IResource resource
, TraceTypeHelper traceType
) throws CoreException
{
273 return setTraceType(resource
, traceType
, true);
276 * Set the trace type of a {@link TraceTypeHelper}. Should only be
277 * used internally by this project.
280 * the resource to set
282 * the {@link TraceTypeHelper} to set the trace type to.
284 * Flag for refreshing the project
285 * @return Status.OK_Status if successful, error is otherwise.
286 * @throws CoreException
287 * An exception caused by accessing eclipse project items.
289 public static IStatus
setTraceType(IResource resource
, TraceTypeHelper traceType
, boolean refresh
) throws CoreException
{
290 String traceTypeId
= traceType
.getTraceTypeId();
292 resource
.setPersistentProperty(TmfCommonConstants
.TRACETYPE
, traceTypeId
);
294 TmfProjectElement tmfProject
= TmfProjectRegistry
.getProject(resource
.getProject(), true);
295 if (tmfProject
.getTracesFolder().getPath().isPrefixOf(resource
.getFullPath())) {
296 String elementPath
= resource
.getFullPath().makeRelativeTo(tmfProject
.getTracesFolder().getPath()).toString();
297 refreshTraceElement(tmfProject
.getTracesFolder().getTraces(), elementPath
);
298 } else if (resource
.getParent().equals(tmfProject
.getExperimentsFolder().getResource())) {
299 /* The trace type to set is for an experiment */
300 for (TmfExperimentElement experimentElement
: tmfProject
.getExperimentsFolder().getExperiments()) {
301 if (resource
.equals(experimentElement
.getResource())) {
302 experimentElement
.refreshTraceType();
307 for (TmfExperimentElement experimentElement
: tmfProject
.getExperimentsFolder().getExperiments()) {
308 if (experimentElement
.getPath().isPrefixOf(resource
.getFullPath())) {
309 String elementPath
= resource
.getFullPath().makeRelativeTo(experimentElement
.getPath()).toString();
310 refreshTraceElement(experimentElement
.getTraces(), elementPath
);
316 tmfProject
.refresh();
318 return Status
.OK_STATUS
;
321 private static void refreshTraceElement(List
<TmfTraceElement
> traceElements
, String elementPath
) {
322 for (TmfTraceElement traceElement
: traceElements
) {
323 if (traceElement
.getElementPath().equals(elementPath
)) {
324 traceElement
.refreshTraceType();
331 * Retrieves all configuration elements from the platform extension registry
332 * for the trace type UI extension.
335 * The type of trace type requested, either TRACE or EXPERIMENT
336 * @return An array of trace type configuration elements
338 public static IConfigurationElement
[] getTypeUIElements(TraceElementType elType
) {
339 String elementName
= TYPE_ELEM
;
340 if (elType
== TraceElementType
.EXPERIMENT
) {
341 elementName
= EXPERIMENT_ELEM
;
343 IConfigurationElement
[] elements
=
344 Platform
.getExtensionRegistry().getConfigurationElementsFor(TMF_TRACE_TYPE_UI_ID
);
345 List
<IConfigurationElement
> typeElements
= new LinkedList
<>();
346 for (IConfigurationElement element
: elements
) {
347 if (element
.getName().equals(elementName
)) {
348 typeElements
.add(element
);
351 return typeElements
.toArray(new IConfigurationElement
[typeElements
.size()]);
355 * Get the UI elements for the given trace type
360 * The type of trace type requested, either TRACE or EXPERIMENT
361 * @return The top-level configuration element (access its children with
362 * .getChildren()). Or null if there is no such element.
365 public static IConfigurationElement
getTraceUIAttributes(String traceType
, TraceElementType elType
) {
366 IConfigurationElement
[] elements
= getTypeUIElements(elType
);
367 for (IConfigurationElement ce
: elements
) {
368 if (traceType
.equals(ce
.getAttribute(TRACETYPE_ATTR
))) {
376 * Get the Event Table type specified by the trace type's extension point,
380 * The trace for which we want the events table.
382 * The parent composite that the event table will have
384 * The cache size to use with this event table. Should be defined
386 * @return The corresponding Event Table, or 'null' if this trace type did
389 public static @Nullable TmfEventsTable
getEventTable(ITmfTrace trace
, Composite parent
, int cacheSize
) {
390 final String traceType
= getTraceType(trace
);
391 if (traceType
== null) {
395 for (final IConfigurationElement ce
: TmfTraceTypeUIUtils
.getTypeUIElements(TraceElementType
.TRACE
)) {
396 if (ce
.getAttribute(TmfTraceTypeUIUtils
.TRACETYPE_ATTR
).equals(traceType
)) {
397 final IConfigurationElement
[] eventsTableTypeCE
= ce
.getChildren(TmfTraceTypeUIUtils
.EVENTS_TABLE_TYPE_ELEM
);
399 if (eventsTableTypeCE
.length
!= 1) {
402 final String eventsTableType
= eventsTableTypeCE
[0].getAttribute(TmfTraceTypeUIUtils
.CLASS_ATTR
);
403 if (eventsTableType
.isEmpty()) {
407 final Bundle bundle
= Platform
.getBundle(ce
.getContributor().getName());
408 final Class
<?
> c
= bundle
.loadClass(eventsTableType
);
409 final Class
<?
>[] constructorArgs
= new Class
[] { Composite
.class, int.class };
410 final Constructor
<?
> constructor
= c
.getConstructor(constructorArgs
);
411 final Object
[] args
= new Object
[] { parent
, cacheSize
};
412 return (TmfEventsTable
) constructor
.newInstance(args
);
414 } catch (NoSuchMethodException
| ClassNotFoundException
| InstantiationException
|
415 IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e
) {
424 * Get the trace type (as a String) for the given trace
428 * @return The String representing the trace type, or 'null' if this trace
429 * does not advertise it.
431 private static @Nullable String
getTraceType(ITmfTrace trace
) {
432 IResource res
= trace
.getResource();
437 String traceType
= res
.getPersistentProperty(TmfCommonConstants
.TRACETYPE
);
438 /* May be null here too */
441 } catch (CoreException e
) {