3a4271d18b3757d54be3c6ee4d9826afba6c7d9b
[deliverable/tracecompass.git] / 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 *******************************************************************************/
13
14 package org.eclipse.tracecompass.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.Comparator;
20 import java.util.HashMap;
21 import java.util.LinkedList;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.TreeSet;
25
26 import org.eclipse.core.resources.IResource;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.runtime.IConfigurationElement;
29 import org.eclipse.core.runtime.IStatus;
30 import org.eclipse.core.runtime.Platform;
31 import org.eclipse.core.runtime.Status;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.eclipse.osgi.util.NLS;
34 import org.eclipse.swt.SWT;
35 import org.eclipse.swt.events.SelectionEvent;
36 import org.eclipse.swt.events.SelectionListener;
37 import org.eclipse.swt.layout.RowLayout;
38 import org.eclipse.swt.widgets.Button;
39 import org.eclipse.swt.widgets.Composite;
40 import org.eclipse.swt.widgets.Display;
41 import org.eclipse.swt.widgets.Shell;
42 import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
43 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomTxtTrace;
44 import org.eclipse.tracecompass.tmf.core.parsers.custom.CustomXmlTrace;
45 import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceImportException;
46 import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType;
47 import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType.TraceElementType;
48 import org.eclipse.tracecompass.tmf.core.project.model.TraceTypeHelper;
49 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
50 import org.eclipse.tracecompass.tmf.core.util.Pair;
51 import org.eclipse.tracecompass.tmf.ui.viewers.events.TmfEventsTable;
52 import org.osgi.framework.Bundle;
53
54 /**
55 * Utils class for the UI-specific parts of @link {@link TmfTraceType}.
56 *
57 * @author Alexandre Montplaisir
58 * @since 3.0
59 */
60 public final class TmfTraceTypeUIUtils {
61
62 /** Extension point ID */
63 public static final String TMF_TRACE_TYPE_UI_ID = "org.eclipse.linuxtools.tmf.ui.tracetypeui"; //$NON-NLS-1$
64
65 /** Extension point element 'type' (should match the type in TmfTraceType) */
66 public static final String TYPE_ELEM = "type"; //$NON-NLS-1$
67
68 /**
69 * Extension point element 'experiment' (should match the type in
70 * TmfTraceType)
71 */
72 public static final String EXPERIMENT_ELEM = "experiment"; //$NON-NLS-1$
73
74 /** Extension point element 'Default editor' */
75 public static final String DEFAULT_EDITOR_ELEM = "defaultEditor"; //$NON-NLS-1$
76
77 /** Extension point element 'Events table type' */
78 public static final String EVENTS_TABLE_TYPE_ELEM = "eventsTableType"; //$NON-NLS-1$
79
80 /** Extension point element 'Event Table Columns'
81 * @since 3.2*/
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 final String errorMsg = NLS.bind(Messages.TmfOpenTraceHelper_NoTraceTypeMatch, path);
216 throw new TmfTraceImportException(errorMsg);
217 } else if (validCandidates.size() != 1) {
218 List<Pair<Integer, TraceTypeHelper>> candidates = new ArrayList<>(validCandidates);
219 List<Pair<Integer, TraceTypeHelper>> reducedCandidates = reduce(candidates);
220 for (Pair<Integer, TraceTypeHelper> candidatePair : reducedCandidates) {
221 TraceTypeHelper candidate = candidatePair.getSecond();
222 if (candidate.getTraceTypeId().equals(traceTypeHint)) {
223 traceTypeToSet = candidate;
224 break;
225 }
226 }
227 if (traceTypeToSet == null) {
228 if (reducedCandidates.size() == 0) {
229 throw new TmfTraceImportException(Messages.TmfOpenTraceHelper_ReduceError);
230 } else if (reducedCandidates.size() == 1) {
231 traceTypeToSet = reducedCandidates.get(0).getSecond();
232 } else if (shell == null) {
233 Pair<Integer, TraceTypeHelper> candidate = reducedCandidates.get(0);
234 // if the best match has lowest confidence, don't select it
235 if (candidate.getFirst() > 0) {
236 traceTypeToSet = candidate.getSecond();
237 }
238 } else {
239 traceTypeToSet = getTraceTypeToSet(reducedCandidates, shell);
240 }
241 }
242 } else {
243 traceTypeToSet = validCandidates.first().getSecond();
244 }
245 return traceTypeToSet;
246 }
247
248 /**
249 * Set the trace type of a {@link TraceTypeHelper}. Should only be
250 * used internally by this project.
251 *
252 * @param resource
253 * the resource to set
254 * @param traceType
255 * the {@link TraceTypeHelper} to set the trace type to.
256 * @return Status.OK_Status if successful, error is otherwise.
257 * @throws CoreException
258 * An exception caused by accessing eclipse project items.
259 */
260 public static IStatus setTraceType(IResource resource, TraceTypeHelper traceType) throws CoreException {
261 return setTraceType(resource, traceType, true);
262 }
263 /**
264 * Set the trace type of a {@link TraceTypeHelper}. Should only be
265 * used internally by this project.
266 *
267 * @param resource
268 * the resource to set
269 * @param traceType
270 * the {@link TraceTypeHelper} to set the trace type to.
271 * @param refresh
272 * Flag for refreshing the project
273 * @return Status.OK_Status if successful, error is otherwise.
274 * @throws CoreException
275 * An exception caused by accessing eclipse project items.
276 * @since 3.1
277 */
278 public static IStatus setTraceType(IResource resource, TraceTypeHelper traceType, boolean refresh) throws CoreException {
279 String traceTypeId = traceType.getTraceTypeId();
280
281 resource.setPersistentProperty(TmfCommonConstants.TRACETYPE, traceTypeId);
282
283 TmfProjectElement tmfProject = TmfProjectRegistry.getProject(resource.getProject(), true);
284 if (tmfProject.getTracesFolder().getPath().isPrefixOf(resource.getFullPath())) {
285 String elementPath = resource.getFullPath().makeRelativeTo(tmfProject.getTracesFolder().getPath()).toString();
286 refreshTraceElement(tmfProject.getTracesFolder().getTraces(), elementPath);
287 } else if (resource.getParent().equals(tmfProject.getExperimentsFolder().getResource())) {
288 /* The trace type to set is for an experiment */
289 for (TmfExperimentElement experimentElement : tmfProject.getExperimentsFolder().getExperiments()) {
290 if (resource.equals(experimentElement.getResource())) {
291 experimentElement.refreshTraceType();
292 break;
293 }
294 }
295 } else {
296 for (TmfExperimentElement experimentElement : tmfProject.getExperimentsFolder().getExperiments()) {
297 if (experimentElement.getPath().isPrefixOf(resource.getFullPath())) {
298 String elementPath = resource.getFullPath().makeRelativeTo(experimentElement.getPath()).toString();
299 refreshTraceElement(experimentElement.getTraces(), elementPath);
300 break;
301 }
302 }
303 }
304 if (refresh) {
305 tmfProject.refresh();
306 }
307 return Status.OK_STATUS;
308 }
309
310 private static void refreshTraceElement(List<TmfTraceElement> traceElements, String elementPath) {
311 for (TmfTraceElement traceElement : traceElements) {
312 if (traceElement.getElementPath().equals(elementPath)) {
313 traceElement.refreshTraceType();
314 break;
315 }
316 }
317 }
318
319 /**
320 * Retrieves all configuration elements from the platform extension registry
321 * for the trace type UI extension.
322 *
323 * @param elType
324 * The type of trace type requested, either TRACE or EXPERIMENT
325 * @return An array of trace type configuration elements
326 */
327 public static IConfigurationElement[] getTypeUIElements(TraceElementType elType) {
328 String elementName = TYPE_ELEM;
329 if (elType == TraceElementType.EXPERIMENT) {
330 elementName = EXPERIMENT_ELEM;
331 }
332 IConfigurationElement[] elements =
333 Platform.getExtensionRegistry().getConfigurationElementsFor(TMF_TRACE_TYPE_UI_ID);
334 List<IConfigurationElement> typeElements = new LinkedList<>();
335 for (IConfigurationElement element : elements) {
336 if (element.getName().equals(elementName)) {
337 typeElements.add(element);
338 }
339 }
340 return typeElements.toArray(new IConfigurationElement[typeElements.size()]);
341 }
342
343 /**
344 * Get the UI elements for the given trace type
345 *
346 * @param traceType
347 * The tracetype ID
348 * @param elType
349 * The type of trace type requested, either TRACE or EXPERIMENT
350 * @return The top-level configuration element (access its children with
351 * .getChildren()). Or null if there is no such element.
352 */
353 @Nullable
354 public static IConfigurationElement getTraceUIAttributes(String traceType, TraceElementType elType) {
355 IConfigurationElement[] elements = getTypeUIElements(elType);
356 for (IConfigurationElement ce : elements) {
357 if (traceType.equals(ce.getAttribute(TRACETYPE_ATTR))) {
358 return ce;
359 }
360 }
361 return null;
362 }
363
364 /**
365 * Get the Event Table type specified by the trace type's extension point,
366 * if there is one.
367 *
368 * @param trace
369 * The trace for which we want the events table.
370 * @param parent
371 * The parent composite that the event table will have
372 * @param cacheSize
373 * The cache size to use with this event table. Should be defined
374 * by the trace type.
375 * @return The corresponding Event Table, or 'null' if this trace type did
376 * not specify any.
377 * @since 3.2
378 */
379 public static @Nullable TmfEventsTable getEventTable(ITmfTrace trace, Composite parent, int cacheSize) {
380 final String traceType = getTraceType(trace);
381 if (traceType == null) {
382 return null;
383 }
384
385 for (final IConfigurationElement ce : TmfTraceTypeUIUtils.getTypeUIElements(TraceElementType.TRACE)) {
386 if (ce.getAttribute(TmfTraceTypeUIUtils.TRACETYPE_ATTR).equals(traceType)) {
387 final IConfigurationElement[] eventsTableTypeCE = ce.getChildren(TmfTraceTypeUIUtils.EVENTS_TABLE_TYPE_ELEM);
388
389 if (eventsTableTypeCE.length != 1) {
390 break;
391 }
392 final String eventsTableType = eventsTableTypeCE[0].getAttribute(TmfTraceTypeUIUtils.CLASS_ATTR);
393 if (eventsTableType.isEmpty()) {
394 break;
395 }
396 try {
397 final Bundle bundle = Platform.getBundle(ce.getContributor().getName());
398 final Class<?> c = bundle.loadClass(eventsTableType);
399 final Class<?>[] constructorArgs = new Class[] { Composite.class, int.class };
400 final Constructor<?> constructor = c.getConstructor(constructorArgs);
401 final Object[] args = new Object[] { parent, cacheSize };
402 return (TmfEventsTable) constructor.newInstance(args);
403
404 } catch (NoSuchMethodException | ClassNotFoundException | InstantiationException |
405 IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
406 return null;
407 }
408 }
409 }
410 return null;
411 }
412
413 /**
414 * Get the trace type (as a String) for the given trace
415 *
416 * @param trace
417 * The trace object
418 * @return The String representing the trace type, or 'null' if this trace
419 * does not advertise it.
420 */
421 private static @Nullable String getTraceType(ITmfTrace trace) {
422 IResource res = trace.getResource();
423 if (res == null) {
424 return null;
425 }
426 try {
427 String traceType = res.getPersistentProperty(TmfCommonConstants.TRACETYPE);
428 /* May be null here too */
429 return traceType;
430
431 } catch (CoreException e) {
432 return null;
433 }
434 }
435 }
This page took 0.043092 seconds and 4 git commands to generate.