tmf: Move plugins to the Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / project / model / TmfTraceTypeUIUtils.java
1 /*******************************************************************************
2 * Copyright (c) 2014 Ericsson
3 *
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
8 *
9 * Contributors:
10 * Alexandre Montplaisir - Initial API and implementation
11 * Patrick Tasse - Add support for folder elements
12 *******************************************************************************/
13
14 package org.eclipse.linuxtools.tmf.ui.project.model;
15
16 import java.lang.reflect.Constructor;
17 import java.lang.reflect.InvocationTargetException;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Comparator;
21 import java.util.HashMap;
22 import java.util.LinkedList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.TreeSet;
26
27 import org.eclipse.core.resources.IResource;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IConfigurationElement;
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.core.runtime.Platform;
32 import org.eclipse.core.runtime.Status;
33 import org.eclipse.jdt.annotation.Nullable;
34 import org.eclipse.linuxtools.internal.tmf.ui.parsers.custom.CustomEventTableColumns;
35 import org.eclipse.linuxtools.tmf.core.TmfCommonConstants;
36 import org.eclipse.linuxtools.tmf.core.parsers.custom.CustomTxtTrace;
37 import org.eclipse.linuxtools.tmf.core.parsers.custom.CustomXmlTrace;
38 import org.eclipse.linuxtools.tmf.core.project.model.TmfTraceImportException;
39 import org.eclipse.linuxtools.tmf.core.project.model.TmfTraceType;
40 import org.eclipse.linuxtools.tmf.core.project.model.TmfTraceType.TraceElementType;
41 import org.eclipse.linuxtools.tmf.core.project.model.TraceTypeHelper;
42 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
43 import org.eclipse.linuxtools.tmf.core.util.Pair;
44 import org.eclipse.linuxtools.tmf.ui.viewers.events.TmfEventsTable;
45 import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.ITmfEventTableColumns;
46 import org.eclipse.linuxtools.tmf.ui.viewers.events.columns.TmfEventTableColumn;
47 import org.eclipse.osgi.util.NLS;
48 import org.eclipse.swt.SWT;
49 import org.eclipse.swt.events.SelectionEvent;
50 import org.eclipse.swt.events.SelectionListener;
51 import org.eclipse.swt.layout.RowLayout;
52 import org.eclipse.swt.widgets.Button;
53 import org.eclipse.swt.widgets.Composite;
54 import org.eclipse.swt.widgets.Display;
55 import org.eclipse.swt.widgets.Shell;
56 import org.osgi.framework.Bundle;
57
58 /**
59 * Utils class for the UI-specific parts of @link {@link TmfTraceType}.
60 *
61 * @author Alexandre Montplaisir
62 * @since 3.0
63 */
64 public final class TmfTraceTypeUIUtils {
65
66 /** Extension point ID */
67 public static final String TMF_TRACE_TYPE_UI_ID = "org.eclipse.linuxtools.tmf.ui.tracetypeui"; //$NON-NLS-1$
68
69 /** Extension point element 'type' (should match the type in TmfTraceType) */
70 public static final String TYPE_ELEM = "type"; //$NON-NLS-1$
71
72 /**
73 * Extension point element 'experiment' (should match the type in
74 * TmfTraceType)
75 */
76 public static final String EXPERIMENT_ELEM = "experiment"; //$NON-NLS-1$
77
78 /** Extension point element 'Default editor' */
79 public static final String DEFAULT_EDITOR_ELEM = "defaultEditor"; //$NON-NLS-1$
80
81 /** Extension point element 'Events table type' */
82 public static final String EVENTS_TABLE_TYPE_ELEM = "eventsTableType"; //$NON-NLS-1$
83
84 /** Extension point element 'Event Table Columns'
85 * @since 3.2*/
86 public static final String EVENT_TABLE_COLUMNS = "eventTableColumns"; //$NON-NLS-1$
87
88 /** Extension point attribute 'tracetype' */
89 public static final String TRACETYPE_ATTR = "tracetype"; //$NON-NLS-1$
90
91 /** Extension point attribute 'icon' */
92 public static final String ICON_ATTR = "icon"; //$NON-NLS-1$
93
94 /** Extension point attribute 'class' (attribute of other elements) */
95 public static final String CLASS_ATTR = "class"; //$NON-NLS-1$
96
97 private TmfTraceTypeUIUtils() {
98 }
99
100 private static List<Pair<Integer, TraceTypeHelper>> reduce(List<Pair<Integer, TraceTypeHelper>> candidates) {
101 List<Pair<Integer, TraceTypeHelper>> retVal = new ArrayList<>();
102
103 // get all the tracetypes that are unique in that stage
104 for (Pair<Integer, TraceTypeHelper> candidatePair : candidates) {
105 TraceTypeHelper candidate = candidatePair.getSecond();
106 if (isUnique(candidate, candidates)) {
107 retVal.add(candidatePair);
108 }
109 }
110 return retVal;
111 }
112
113 /*
114 * Only return the leaves of the trace types. Ignore custom trace types.
115 */
116 private static boolean isUnique(TraceTypeHelper trace, List<Pair<Integer, TraceTypeHelper>> set) {
117 if (trace.getTraceClass().equals(CustomTxtTrace.class) ||
118 trace.getTraceClass().equals(CustomXmlTrace.class)) {
119 return true;
120 }
121 // check if the trace type is the leaf. we make an instance of the trace
122 // type and if it is only an instance of itself, it is a leaf
123 final ITmfTrace tmfTrace = trace.getTrace();
124 int count = -1;
125 for (Pair<Integer, TraceTypeHelper> child : set) {
126 final ITmfTrace traceCandidate = child.getSecond().getTrace();
127 if (tmfTrace.getClass().isInstance(traceCandidate)) {
128 count++;
129 }
130 }
131 return count == 0;
132 }
133
134 private static TraceTypeHelper getTraceTypeToSet(List<Pair<Integer, TraceTypeHelper>> candidates, Shell shell) {
135 final Map<String, String> names = new HashMap<>();
136 Shell shellToShow = new Shell(shell);
137 shellToShow.setText(Messages.TmfTraceType_SelectTraceType);
138 final String candidatesToSet[] = new String[1];
139 for (Pair<Integer, TraceTypeHelper> candidatePair : candidates) {
140 TraceTypeHelper candidate = candidatePair.getSecond();
141 Button b = new Button(shellToShow, SWT.RADIO);
142 final String displayName = candidate.getCategoryName() + ':' + candidate.getName();
143 b.setText(displayName);
144 names.put(displayName, candidate.getCanonicalName());
145
146 b.addSelectionListener(new SelectionListener() {
147
148 @Override
149 public void widgetSelected(SelectionEvent e) {
150 final Button source = (Button) e.getSource();
151 candidatesToSet[0] = (names.get(source.getText()));
152 source.getParent().dispose();
153 }
154
155 @Override
156 public void widgetDefaultSelected(SelectionEvent e) {
157
158 }
159 });
160 }
161 shellToShow.setLayout(new RowLayout(SWT.VERTICAL));
162 shellToShow.pack();
163 shellToShow.open();
164
165 Display display = shellToShow.getDisplay();
166 while (!shellToShow.isDisposed()) {
167 if (!display.readAndDispatch()) {
168 display.sleep();
169 }
170 }
171 return TmfTraceType.getTraceType(candidatesToSet[0]);
172 }
173
174 /**
175 * This member figures out the trace type of a given file. It will prompt
176 * the user if it needs more information to properly pick the trace type.
177 *
178 * @param path
179 * The path of file to import
180 * @param shell
181 * a shell to display the message to. If it is null, it is
182 * assumed to be cancelled.
183 * @param traceTypeHint
184 * the ID of a trace (like "o.e.l.specifictrace" )
185 * @return null if the request is cancelled or a TraceTypeHelper if it
186 * passes.
187 * @throws TmfTraceImportException
188 * if the traces don't match or there are errors in the trace
189 * file
190 */
191 public static TraceTypeHelper selectTraceType(String path, Shell shell, String traceTypeHint) throws TmfTraceImportException {
192
193 Comparator<Pair<Integer, TraceTypeHelper>> comparator = new Comparator<Pair<Integer, TraceTypeHelper>>() {
194 @Override
195 public int compare(Pair<Integer, TraceTypeHelper> o1, Pair<Integer, TraceTypeHelper> o2) {
196 int res = -o1.getFirst().compareTo(o2.getFirst()); // invert so that highest confidence is first
197 if (res == 0) {
198 res = o1.getSecond().getName().compareTo(o2.getSecond().getName());
199 }
200 return res;
201 }
202 };
203 TreeSet<Pair<Integer, TraceTypeHelper>> validCandidates = new TreeSet<>(comparator);
204 final Iterable<TraceTypeHelper> traceTypeHelpers = TmfTraceType.getTraceTypeHelpers();
205 for (TraceTypeHelper traceTypeHelper : traceTypeHelpers) {
206 if (traceTypeHelper.isExperimentType()) {
207 continue;
208 }
209 int confidence = traceTypeHelper.validateWithConfidence(path);
210 if (confidence >= 0) {
211 // insert in the tree map, ordered by confidence (highest confidence first) then name
212 Pair<Integer, TraceTypeHelper> element = new Pair<>(confidence, traceTypeHelper);
213 validCandidates.add(element);
214 }
215 }
216
217 TraceTypeHelper traceTypeToSet = null;
218 if (validCandidates.isEmpty()) {
219 final String errorMsg = NLS.bind(Messages.TmfOpenTraceHelper_NoTraceTypeMatch, path);
220 throw new TmfTraceImportException(errorMsg);
221 } else if (validCandidates.size() != 1) {
222 List<Pair<Integer, TraceTypeHelper>> candidates = new ArrayList<>(validCandidates);
223 List<Pair<Integer, TraceTypeHelper>> reducedCandidates = reduce(candidates);
224 for (Pair<Integer, TraceTypeHelper> candidatePair : reducedCandidates) {
225 TraceTypeHelper candidate = candidatePair.getSecond();
226 if (candidate.getCanonicalName().equals(traceTypeHint)) {
227 traceTypeToSet = candidate;
228 break;
229 }
230 }
231 if (traceTypeToSet == null) {
232 if (reducedCandidates.size() == 0) {
233 throw new TmfTraceImportException(Messages.TmfOpenTraceHelper_ReduceError);
234 } else if (reducedCandidates.size() == 1) {
235 traceTypeToSet = reducedCandidates.get(0).getSecond();
236 } else if (shell == null) {
237 Pair<Integer, TraceTypeHelper> candidate = reducedCandidates.get(0);
238 // if the best match has lowest confidence, don't select it
239 if (candidate.getFirst() > 0) {
240 traceTypeToSet = candidate.getSecond();
241 }
242 } else {
243 traceTypeToSet = getTraceTypeToSet(reducedCandidates, shell);
244 }
245 }
246 } else {
247 traceTypeToSet = validCandidates.first().getSecond();
248 }
249 return traceTypeToSet;
250 }
251
252 /**
253 * Set the trace type of a {@link TraceTypeHelper}. Should only be
254 * used internally by this project.
255 *
256 * @param resource
257 * the resource to set
258 * @param traceType
259 * the {@link TraceTypeHelper} to set the trace type to.
260 * @return Status.OK_Status if successful, error is otherwise.
261 * @throws CoreException
262 * An exception caused by accessing eclipse project items.
263 */
264 public static IStatus setTraceType(IResource resource, TraceTypeHelper traceType) throws CoreException {
265 return setTraceType(resource, traceType, true);
266 }
267 /**
268 * Set the trace type of a {@link TraceTypeHelper}. Should only be
269 * used internally by this project.
270 *
271 * @param resource
272 * the resource to set
273 * @param traceType
274 * the {@link TraceTypeHelper} to set the trace type to.
275 * @param refresh
276 * Flag for refreshing the project
277 * @return Status.OK_Status if successful, error is otherwise.
278 * @throws CoreException
279 * An exception caused by accessing eclipse project items.
280 * @since 3.1
281 */
282 public static IStatus setTraceType(IResource resource, TraceTypeHelper traceType, boolean refresh) throws CoreException {
283 String traceTypeId = traceType.getCanonicalName();
284
285 resource.setPersistentProperty(TmfCommonConstants.TRACETYPE, traceTypeId);
286
287 TmfProjectElement tmfProject = TmfProjectRegistry.getProject(resource.getProject(), true);
288 if (tmfProject.getTracesFolder().getPath().isPrefixOf(resource.getFullPath())) {
289 String elementPath = resource.getFullPath().makeRelativeTo(tmfProject.getTracesFolder().getPath()).toString();
290 refreshTraceElement(tmfProject.getTracesFolder().getTraces(), elementPath);
291 } else if (resource.getParent().equals(tmfProject.getExperimentsFolder().getResource())) {
292 /* The trace type to set is for an experiment */
293 for (TmfExperimentElement experimentElement : tmfProject.getExperimentsFolder().getExperiments()) {
294 if (resource.equals(experimentElement.getResource())) {
295 experimentElement.refreshTraceType();
296 break;
297 }
298 }
299 } else {
300 for (TmfExperimentElement experimentElement : tmfProject.getExperimentsFolder().getExperiments()) {
301 if (experimentElement.getPath().isPrefixOf(resource.getFullPath())) {
302 String elementPath = resource.getFullPath().makeRelativeTo(experimentElement.getPath()).toString();
303 refreshTraceElement(experimentElement.getTraces(), elementPath);
304 break;
305 }
306 }
307 }
308 if (refresh) {
309 tmfProject.refresh();
310 }
311 return Status.OK_STATUS;
312 }
313
314 private static void refreshTraceElement(List<TmfTraceElement> traceElements, String elementPath) {
315 for (TmfTraceElement traceElement : traceElements) {
316 if (traceElement.getElementPath().equals(elementPath)) {
317 traceElement.refreshTraceType();
318 break;
319 }
320 }
321 }
322
323 /**
324 * Retrieves all configuration elements from the platform extension registry
325 * for the trace type UI extension.
326 *
327 * @param elType
328 * The type of trace type requested, either TRACE or EXPERIMENT
329 * @return An array of trace type configuration elements
330 */
331 public static IConfigurationElement[] getTypeUIElements(TraceElementType elType) {
332 String elementName = TYPE_ELEM;
333 if (elType == TraceElementType.EXPERIMENT) {
334 elementName = EXPERIMENT_ELEM;
335 }
336 IConfigurationElement[] elements =
337 Platform.getExtensionRegistry().getConfigurationElementsFor(TMF_TRACE_TYPE_UI_ID);
338 List<IConfigurationElement> typeElements = new LinkedList<>();
339 for (IConfigurationElement element : elements) {
340 if (element.getName().equals(elementName)) {
341 typeElements.add(element);
342 }
343 }
344 return typeElements.toArray(new IConfigurationElement[typeElements.size()]);
345 }
346
347 /**
348 * Get the UI elements for the given trace type
349 *
350 * @param traceType
351 * The tracetype ID
352 * @param elType
353 * The type of trace type requested, either TRACE or EXPERIMENT
354 * @return The top-level configuration element (access its children with
355 * .getChildren()). Or null if there is no such element.
356 */
357 @Nullable
358 public static IConfigurationElement getTraceUIAttributes(String traceType, TraceElementType elType) {
359 IConfigurationElement[] elements = getTypeUIElements(elType);
360 for (IConfigurationElement ce : elements) {
361 if (traceType.equals(ce.getAttribute(TRACETYPE_ATTR))) {
362 return ce;
363 }
364 }
365 return null;
366 }
367
368 /**
369 * Get the Event Table type specified by the trace type's extension point,
370 * if there is one.
371 *
372 * @param trace
373 * The trace for which we want the events table.
374 * @param parent
375 * The parent composite that the event table will have
376 * @param cacheSize
377 * The cache size to use with this event table. Should be defined
378 * by the trace type.
379 * @return The corresponding Event Table, or 'null' if this trace type did
380 * not specify any.
381 * @since 3.2
382 */
383 public static @Nullable TmfEventsTable getEventTable(ITmfTrace trace, Composite parent, int cacheSize) {
384 final String traceType = getTraceType(trace);
385 if (traceType == null) {
386 return null;
387 }
388
389 for (final IConfigurationElement ce : TmfTraceTypeUIUtils.getTypeUIElements(TraceElementType.TRACE)) {
390 if (ce.getAttribute(TmfTraceTypeUIUtils.TRACETYPE_ATTR).equals(traceType)) {
391 final IConfigurationElement[] eventsTableTypeCE = ce.getChildren(TmfTraceTypeUIUtils.EVENTS_TABLE_TYPE_ELEM);
392
393 if (eventsTableTypeCE.length != 1) {
394 break;
395 }
396 final String eventsTableType = eventsTableTypeCE[0].getAttribute(TmfTraceTypeUIUtils.CLASS_ATTR);
397 if (eventsTableType.isEmpty()) {
398 break;
399 }
400 try {
401 final Bundle bundle = Platform.getBundle(ce.getContributor().getName());
402 final Class<?> c = bundle.loadClass(eventsTableType);
403 final Class<?>[] constructorArgs = new Class[] { Composite.class, int.class };
404 final Constructor<?> constructor = c.getConstructor(constructorArgs);
405 final Object[] args = new Object[] { parent, cacheSize };
406 return (TmfEventsTable) constructor.newInstance(args);
407
408 } catch (NoSuchMethodException | ClassNotFoundException | InstantiationException |
409 IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
410 return null;
411 }
412 }
413 }
414 return null;
415 }
416
417 /**
418 * Get the Event Table columns specified by the trace type's extension
419 * point, if there are any.
420 *
421 * @param trace
422 * The trace for which we want the columns.
423 * @return The corresponding event table columns, or 'null' if this trace
424 * type did not specify any.
425 * @since 3.2
426 */
427 public static @Nullable Collection<? extends TmfEventTableColumn> getEventTableColumns(ITmfTrace trace) {
428 final String traceType = getTraceType(trace);
429 if (traceType == null) {
430 return null;
431 }
432
433 /*
434 * Custom traces are a special case : the columns are defined by the
435 * trace definition.
436 */
437 if (traceType.startsWith(CustomTxtTrace.class.getCanonicalName())) {
438 return CustomEventTableColumns.generateColumns(((CustomTxtTrace) trace).getDefinition());
439 }
440 if (traceType.startsWith(CustomXmlTrace.class.getCanonicalName())) {
441 return CustomEventTableColumns.generateColumns(((CustomXmlTrace) trace).getDefinition());
442 }
443
444 /* For all other trace types, we will go look into the extension point */
445 for (final IConfigurationElement ce : TmfTraceTypeUIUtils.getTypeUIElements(TraceElementType.TRACE)) {
446 if (ce.getAttribute(TmfTraceTypeUIUtils.TRACETYPE_ATTR).equals(traceType)) {
447 final IConfigurationElement[] eventTableColumnsCE = ce.getChildren(TmfTraceTypeUIUtils.EVENT_TABLE_COLUMNS);
448
449 if (eventTableColumnsCE.length != 1) {
450 break;
451 }
452 final String eventTableColumnsClass = eventTableColumnsCE[0].getAttribute(TmfTraceTypeUIUtils.CLASS_ATTR);
453 if ((eventTableColumnsClass == null) || (eventTableColumnsClass.isEmpty())) {
454 break;
455 }
456 try {
457 final Bundle bundle = Platform.getBundle(ce.getContributor().getName());
458 final Class<?> c = bundle.loadClass(eventTableColumnsClass);
459 final Constructor<?> ctor = c.getConstructor();
460 ITmfEventTableColumns cols = (ITmfEventTableColumns) ctor.newInstance();
461 return cols.getEventTableColumns();
462
463 } catch (NoSuchMethodException | ClassNotFoundException | InstantiationException |
464 IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
465 return null;
466 }
467 }
468 }
469 return null;
470 }
471
472 /**
473 * Get the trace type (as a String) for the given trace
474 *
475 * @param trace
476 * The trace object
477 * @return The String representing the trace type, or 'null' if this trace
478 * does not advertise it.
479 */
480 private static @Nullable String getTraceType(ITmfTrace trace) {
481 IResource res = trace.getResource();
482 if (res == null) {
483 return null;
484 }
485 try {
486 String traceType = res.getPersistentProperty(TmfCommonConstants.TRACETYPE);
487 /* May be null here too */
488 return traceType;
489
490 } catch (CoreException e) {
491 return null;
492 }
493 }
494 }
This page took 0.04373 seconds and 5 git commands to generate.