tmf: Move plugins to their own sub-directory
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / tmf / ui / project / model / TmfTraceTypeUIUtils.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 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 * Bernd Hufmann - Update trace type auto-detection
13 *******************************************************************************/
14
15 package org.eclipse.tracecompass.tmf.ui.project.model;
16
17 import java.io.File;
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;
25 import java.util.Map;
26 import java.util.TreeSet;
27
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;
55
56 /**
57 * Utils class for the UI-specific parts of @link {@link TmfTraceType}.
58 *
59 * @author Alexandre Montplaisir
60 */
61 public final class TmfTraceTypeUIUtils {
62
63 /** Extension point ID */
64 public static final String TMF_TRACE_TYPE_UI_ID = "org.eclipse.linuxtools.tmf.ui.tracetypeui"; //$NON-NLS-1$
65
66 /** Extension point element 'type' (should match the type in TmfTraceType) */
67 public static final String TYPE_ELEM = "type"; //$NON-NLS-1$
68
69 /**
70 * Extension point element 'experiment' (should match the type in
71 * TmfTraceType)
72 */
73 public static final String EXPERIMENT_ELEM = "experiment"; //$NON-NLS-1$
74
75 /** Extension point element 'Default editor' */
76 public static final String DEFAULT_EDITOR_ELEM = "defaultEditor"; //$NON-NLS-1$
77
78 /** Extension point element 'Events table type' */
79 public static final String EVENTS_TABLE_TYPE_ELEM = "eventsTableType"; //$NON-NLS-1$
80
81 /** Extension point element 'Event Table Columns' */
82 public static final String EVENT_TABLE_COLUMNS = "eventTableColumns"; //$NON-NLS-1$
83
84 /** Extension point attribute 'tracetype' */
85 public static final String TRACETYPE_ATTR = "tracetype"; //$NON-NLS-1$
86
87 /** Extension point attribute 'icon' */
88 public static final String ICON_ATTR = "icon"; //$NON-NLS-1$
89
90 /** Extension point attribute 'class' (attribute of other elements) */
91 public static final String CLASS_ATTR = "class"; //$NON-NLS-1$
92
93 private TmfTraceTypeUIUtils() {
94 }
95
96 private static List<Pair<Integer, TraceTypeHelper>> reduce(List<Pair<Integer, TraceTypeHelper>> candidates) {
97 List<Pair<Integer, TraceTypeHelper>> retVal = new ArrayList<>();
98
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);
104 }
105 }
106 return retVal;
107 }
108
109 /*
110 * Only return the leaves of the trace types. Ignore custom trace types.
111 */
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)) {
115 return true;
116 }
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();
120 int count = -1;
121 for (Pair<Integer, TraceTypeHelper> child : set) {
122 final ITmfTrace traceCandidate = child.getSecond().getTrace();
123 if (tmfTrace.getClass().isInstance(traceCandidate)) {
124 count++;
125 }
126 }
127 return count == 0;
128 }
129
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());
141
142 b.addSelectionListener(new SelectionListener() {
143
144 @Override
145 public void widgetSelected(SelectionEvent e) {
146 final Button source = (Button) e.getSource();
147 candidatesToSet[0] = (names.get(source.getText()));
148 source.getParent().dispose();
149 }
150
151 @Override
152 public void widgetDefaultSelected(SelectionEvent e) {
153
154 }
155 });
156 }
157 shellToShow.setLayout(new RowLayout(SWT.VERTICAL));
158 shellToShow.pack();
159 shellToShow.open();
160
161 Display display = shellToShow.getDisplay();
162 while (!shellToShow.isDisposed()) {
163 if (!display.readAndDispatch()) {
164 display.sleep();
165 }
166 }
167 return TmfTraceType.getTraceType(candidatesToSet[0]);
168 }
169
170 /**
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.
173 *
174 * @param path
175 * The path of file to import
176 * @param shell
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
182 * passes.
183 * @throws TmfTraceImportException
184 * if the traces don't match or there are errors in the trace
185 * file
186 */
187 public static TraceTypeHelper selectTraceType(String path, Shell shell, String traceTypeHint) throws TmfTraceImportException {
188
189 Comparator<Pair<Integer, TraceTypeHelper>> comparator = new Comparator<Pair<Integer, TraceTypeHelper>>() {
190 @Override
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
193 if (res == 0) {
194 res = o1.getSecond().getName().compareTo(o2.getSecond().getName());
195 }
196 return res;
197 }
198 };
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()) {
203 continue;
204 }
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);
210 }
211 }
212
213 TraceTypeHelper traceTypeToSet = null;
214 if (validCandidates.isEmpty()) {
215 File traceFile = new File(path);
216 if (traceFile.isFile()) {
217 return null;
218 }
219 final String errorMsg = NLS.bind(Messages.TmfOpenTraceHelper_NoTraceTypeMatch, path);
220 throw new TmfTraceImportException(errorMsg);
221 }
222
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;
230 break;
231 }
232 }
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();
240 }
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();
246 }
247 } else {
248 traceTypeToSet = getTraceTypeToSet(reducedCandidates, shell);
249 }
250 }
251 } else {
252 // Don't select the trace type if it has the lowest confidence
253 if (validCandidates.first().getFirst() > 0) {
254 traceTypeToSet = validCandidates.first().getSecond();
255 }
256 }
257 return traceTypeToSet;
258 }
259
260 /**
261 * Set the trace type of a {@link TraceTypeHelper}. Should only be
262 * used internally by this project.
263 *
264 * @param resource
265 * the resource to set
266 * @param traceType
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.
271 */
272 public static IStatus setTraceType(IResource resource, TraceTypeHelper traceType) throws CoreException {
273 return setTraceType(resource, traceType, true);
274 }
275 /**
276 * Set the trace type of a {@link TraceTypeHelper}. Should only be
277 * used internally by this project.
278 *
279 * @param resource
280 * the resource to set
281 * @param traceType
282 * the {@link TraceTypeHelper} to set the trace type to.
283 * @param refresh
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.
288 */
289 public static IStatus setTraceType(IResource resource, TraceTypeHelper traceType, boolean refresh) throws CoreException {
290 String traceTypeId = traceType.getTraceTypeId();
291
292 resource.setPersistentProperty(TmfCommonConstants.TRACETYPE, traceTypeId);
293
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();
303 break;
304 }
305 }
306 } else {
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);
311 break;
312 }
313 }
314 }
315 if (refresh) {
316 tmfProject.refresh();
317 }
318 return Status.OK_STATUS;
319 }
320
321 private static void refreshTraceElement(List<TmfTraceElement> traceElements, String elementPath) {
322 for (TmfTraceElement traceElement : traceElements) {
323 if (traceElement.getElementPath().equals(elementPath)) {
324 traceElement.refreshTraceType();
325 break;
326 }
327 }
328 }
329
330 /**
331 * Retrieves all configuration elements from the platform extension registry
332 * for the trace type UI extension.
333 *
334 * @param elType
335 * The type of trace type requested, either TRACE or EXPERIMENT
336 * @return An array of trace type configuration elements
337 */
338 public static IConfigurationElement[] getTypeUIElements(TraceElementType elType) {
339 String elementName = TYPE_ELEM;
340 if (elType == TraceElementType.EXPERIMENT) {
341 elementName = EXPERIMENT_ELEM;
342 }
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);
349 }
350 }
351 return typeElements.toArray(new IConfigurationElement[typeElements.size()]);
352 }
353
354 /**
355 * Get the UI elements for the given trace type
356 *
357 * @param traceType
358 * The tracetype ID
359 * @param elType
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.
363 */
364 @Nullable
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))) {
369 return ce;
370 }
371 }
372 return null;
373 }
374
375 /**
376 * Get the Event Table type specified by the trace type's extension point,
377 * if there is one.
378 *
379 * @param trace
380 * The trace for which we want the events table.
381 * @param parent
382 * The parent composite that the event table will have
383 * @param cacheSize
384 * The cache size to use with this event table. Should be defined
385 * by the trace type.
386 * @return The corresponding Event Table, or 'null' if this trace type did
387 * not specify any.
388 */
389 public static @Nullable TmfEventsTable getEventTable(ITmfTrace trace, Composite parent, int cacheSize) {
390 final String traceType = getTraceType(trace);
391 if (traceType == null) {
392 return null;
393 }
394
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);
398
399 if (eventsTableTypeCE.length != 1) {
400 break;
401 }
402 final String eventsTableType = eventsTableTypeCE[0].getAttribute(TmfTraceTypeUIUtils.CLASS_ATTR);
403 if (eventsTableType.isEmpty()) {
404 break;
405 }
406 try {
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);
413
414 } catch (NoSuchMethodException | ClassNotFoundException | InstantiationException |
415 IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
416 return null;
417 }
418 }
419 }
420 return null;
421 }
422
423 /**
424 * Get the trace type (as a String) for the given trace
425 *
426 * @param trace
427 * The trace object
428 * @return The String representing the trace type, or 'null' if this trace
429 * does not advertise it.
430 */
431 private static @Nullable String getTraceType(ITmfTrace trace) {
432 IResource res = trace.getResource();
433 if (res == null) {
434 return null;
435 }
436 try {
437 String traceType = res.getPersistentProperty(TmfCommonConstants.TRACETYPE);
438 /* May be null here too */
439 return traceType;
440
441 } catch (CoreException e) {
442 return null;
443 }
444 }
445 }
This page took 0.064319 seconds and 5 git commands to generate.