tmf: Add automatic trace type detection to import trace wizard
[deliverable/tracecompass.git] / org.eclipse.linuxtools.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 *******************************************************************************/
12
13 package org.eclipse.linuxtools.tmf.ui.project.model;
14
15 import java.io.File;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.TreeSet;
24
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.core.resources.ResourcesPlugin;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.runtime.IConfigurationElement;
29 import org.eclipse.core.runtime.IPath;
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.core.runtime.Status;
32 import org.eclipse.linuxtools.internal.tmf.ui.Activator;
33 import org.eclipse.linuxtools.tmf.core.TmfCommonConstants;
34 import org.eclipse.linuxtools.tmf.core.project.model.TmfTraceImportException;
35 import org.eclipse.linuxtools.tmf.core.project.model.TmfTraceType;
36 import org.eclipse.linuxtools.tmf.core.project.model.TraceTypeHelper;
37 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
38 import org.eclipse.linuxtools.tmf.core.util.Pair;
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.events.SelectionEvent;
41 import org.eclipse.swt.events.SelectionListener;
42 import org.eclipse.swt.layout.RowLayout;
43 import org.eclipse.swt.widgets.Button;
44 import org.eclipse.swt.widgets.Display;
45 import org.eclipse.swt.widgets.Shell;
46 import org.eclipse.ui.dialogs.FileSystemElement;
47
48 /**
49 * Utils class for the UI-specific parts of @link {@link TmfTraceType}.
50 *
51 * @author Alexandre Montplaisir
52 * @since 3.0
53 */
54 public final class TmfTraceTypeUIUtils {
55
56 private static final String DEFAULT_TRACE_ICON_PATH = "icons" + File.separator + "elcl16" + File.separator + "trace.gif"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
57
58 private static final char SEPARATOR = ':';
59
60 private TmfTraceTypeUIUtils() {}
61
62 private static List<File> isolateTraces(List<FileSystemElement> selectedResources) {
63 List<File> traces = new ArrayList<>();
64
65 // Get the selection
66 Iterator<FileSystemElement> resources = selectedResources.iterator();
67
68 // Get the sorted list of unique entries
69 Map<String, File> fileSystemObjects = new HashMap<>();
70 while (resources.hasNext()) {
71 File resource = (File) resources.next().getFileSystemObject();
72 String key = resource.getAbsolutePath();
73 fileSystemObjects.put(key, resource);
74 }
75 List<String> files = new ArrayList<>(fileSystemObjects.keySet());
76 Collections.sort(files);
77
78 // After sorting, traces correspond to the unique prefixes
79 String prefix = null;
80 for (int i = 0; i < files.size(); i++) {
81 File file = fileSystemObjects.get(files.get(i));
82 String name = file.getAbsolutePath();
83 if (prefix == null || !name.startsWith(prefix)) {
84 prefix = name; // new prefix
85 traces.add(file);
86 }
87 }
88
89 return traces;
90 }
91
92
93 private static List<Pair<Integer, TraceTypeHelper>> reduce(List<Pair<Integer, TraceTypeHelper>> candidates) {
94 List<Pair<Integer, TraceTypeHelper>> retVal = new ArrayList<>();
95
96 // get all the tracetypes that are unique in that stage
97 for (Pair<Integer, TraceTypeHelper> candidatePair : candidates) {
98 TraceTypeHelper candidate = candidatePair.getSecond();
99 if (isUnique(candidate, candidates)) {
100 retVal.add(candidatePair);
101 }
102 }
103 return retVal;
104 }
105
106 /*
107 * Only return the leaves of the trace types. Ignore custom trace types.
108 */
109 private static boolean isUnique(TraceTypeHelper trace, List<Pair<Integer, TraceTypeHelper>> set) {
110 if (isCustomTraceId(trace.getCanonicalName())) {
111 return true;
112 }
113 // check if the trace type is the leaf. we make an instance of the trace
114 // type and if it is only an instance of itself, it is a leaf
115 final ITmfTrace tmfTrace = trace.getTrace();
116 int count = -1;
117 for (Pair<Integer, TraceTypeHelper> child : set) {
118 final ITmfTrace traceCandidate = child.getSecond().getTrace();
119 if (tmfTrace.getClass().isInstance(traceCandidate)) {
120 count++;
121 }
122 }
123 return count == 0;
124 }
125
126
127 /**
128 * Is the trace type id a custom (user-defined) trace type. These are the
129 * traces like : text and xml defined by the custom trace wizard.
130 *
131 * @param traceTypeId
132 * the trace type id
133 * @return true if the trace is a custom type
134 */
135 private static boolean isCustomTraceId(String traceTypeId) {
136 TraceTypeHelper traceType = TmfTraceType.getInstance().getTraceType(traceTypeId);
137 if (traceType != null) {
138 return TmfTraceType.isCustomTrace(traceType.getCategoryName() + SEPARATOR + traceType.getName());
139 }
140 return false;
141 }
142
143 private static TraceTypeHelper getTraceTypeToSet(TmfTraceType type, List<Pair<Integer, TraceTypeHelper>> candidates, Shell shell) {
144 final Map<String, String> names = new HashMap<>();
145 Shell shellToShow = new Shell(shell);
146 shellToShow.setText(Messages.TmfTraceType_SelectTraceType);
147 final String candidatesToSet[] = new String[1];
148 for (Pair<Integer, TraceTypeHelper> candidatePair : candidates) {
149 TraceTypeHelper candidate = candidatePair.getSecond();
150 Button b = new Button(shellToShow, SWT.RADIO);
151 final String displayName = candidate.getCategoryName() + ':' + candidate.getName();
152 b.setText(displayName);
153 names.put(displayName, candidate.getCanonicalName());
154
155 b.addSelectionListener(new SelectionListener() {
156
157 @Override
158 public void widgetSelected(SelectionEvent e) {
159 final Button source = (Button) e.getSource();
160 candidatesToSet[0] = (names.get(source.getText()));
161 source.getParent().dispose();
162 }
163
164 @Override
165 public void widgetDefaultSelected(SelectionEvent e) {
166
167 }
168 });
169 }
170 shellToShow.setLayout(new RowLayout(SWT.VERTICAL));
171 shellToShow.pack();
172 shellToShow.open();
173
174 Display display = shellToShow.getDisplay();
175 while (!shellToShow.isDisposed()) {
176 if (!display.readAndDispatch()) {
177 display.sleep();
178 }
179 }
180 return type.getTraceTypeHelper(candidatesToSet[0]);
181 }
182
183 /**
184 * validate list of traces with a tracetype
185 *
186 * @param type
187 * The TmfTraceType instance
188 * @param traceTypeName
189 * the trace category (canonical name)
190 * @param selectedResources
191 * List of traces to validate
192 * @return true if all the traces are valid
193 */
194 public static boolean validateTrace(TmfTraceType type, String traceTypeName, List<FileSystemElement> selectedResources) {
195 List<File> traces = isolateTraces(selectedResources);
196 return type.validateTraceFiles(traceTypeName, traces);
197 }
198
199 /**
200 * This member figures out the trace type of a given file. It will prompt
201 * the user if it needs more information to properly pick the trace type.
202 *
203 * @param path
204 * The path of file to import
205 * @param shell
206 * a shell to display the message to. If it is null, it is
207 * assumed to be cancelled.
208 * @param traceTypeHint
209 * the ID of a trace (like "o.e.l.specifictrace" )
210 * @return null if the request is cancelled or a TraceTypeHelper if it
211 * passes.
212 * @throws TmfTraceImportException
213 * if the traces don't match or there are errors in the trace
214 * file
215 */
216 public static TraceTypeHelper selectTraceType(String path, Shell shell, String traceTypeHint) throws TmfTraceImportException {
217 TmfTraceType type = TmfTraceType.getInstance();
218
219 Comparator<Pair<Integer, TraceTypeHelper>> comparator = new Comparator<Pair<Integer, TraceTypeHelper>>() {
220 @Override
221 public int compare(Pair<Integer, TraceTypeHelper> o1, Pair<Integer, TraceTypeHelper> o2) {
222 int res = -o1.getFirst().compareTo(o2.getFirst()); // invert so that highest confidence is first
223 if (res == 0) {
224 res = o1.getSecond().getName().compareTo(o2.getSecond().getName());
225 }
226 return res;
227 }
228 };
229 TreeSet<Pair<Integer, TraceTypeHelper>> validCandidates = new TreeSet<>(comparator);
230 final Iterable<TraceTypeHelper> traceTypeHelpers = type.getTraceTypeHelpers();
231 for (TraceTypeHelper traceTypeHelper : traceTypeHelpers) {
232 int confidence = traceTypeHelper.validateWithConfidence(path);
233 if (confidence >= 0) {
234 // insert in the tree map, ordered by confidence (highest confidence first) then name
235 Pair<Integer, TraceTypeHelper> element = new Pair<>(confidence, traceTypeHelper);
236 validCandidates.add(element);
237 }
238 }
239
240 TraceTypeHelper traceTypeToSet = null;
241 if (validCandidates.isEmpty()) {
242 final String errorMsg = Messages.TmfOpenTraceHelper_NoTraceTypeMatch + path;
243 throw new TmfTraceImportException(errorMsg);
244 } else if (validCandidates.size() != 1) {
245 List<Pair<Integer, TraceTypeHelper>> candidates = new ArrayList<>(validCandidates);
246 List<Pair<Integer, TraceTypeHelper>> reducedCandidates = reduce(candidates);
247 for (Pair<Integer, TraceTypeHelper> candidatePair : reducedCandidates) {
248 TraceTypeHelper candidate = candidatePair.getSecond();
249 if (candidate.getCanonicalName().equals(traceTypeHint)) {
250 traceTypeToSet = candidate;
251 break;
252 }
253 }
254 if (traceTypeToSet == null) {
255 if (reducedCandidates.size() == 0) {
256 throw new TmfTraceImportException(Messages.TmfOpenTraceHelper_ReduceError);
257 } else if (reducedCandidates.size() == 1) {
258 traceTypeToSet = reducedCandidates.get(0).getSecond();
259 } else if (shell == null) {
260 Pair<Integer, TraceTypeHelper> candidate = reducedCandidates.get(0);
261 // if the best match has lowest confidence, don't select it
262 if (candidate.getFirst() > 0) {
263 traceTypeToSet = candidate.getSecond();
264 }
265 } else {
266 traceTypeToSet = getTraceTypeToSet(type, reducedCandidates, shell);
267 }
268 }
269 } else {
270 traceTypeToSet = validCandidates.first().getSecond();
271 }
272 return traceTypeToSet;
273 }
274
275
276 /**
277 * Set the trace type of a {@Link TraceTypeHelper}. Should only be
278 * used internally by this project.
279 *
280 * @param path
281 * the {@link IPath} path of the resource to set
282 * @param traceType
283 * the {@link TraceTypeHelper} to set the trace type to.
284 * @return Status.OK_Status if successful, error is otherwise.
285 * @throws CoreException
286 * An exception caused by accessing eclipse project items.
287 */
288 public static IStatus setTraceType(IPath path, TraceTypeHelper traceType) throws CoreException {
289 IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
290 String traceBundle = null, traceTypeId = traceType.getCanonicalName(), traceIcon = null;
291 if (isCustomTraceId(traceTypeId)) {
292 traceBundle = Activator.getDefault().getBundle().getSymbolicName();
293 traceIcon = DEFAULT_TRACE_ICON_PATH;
294 } else {
295 IConfigurationElement ce = TmfTraceType.getInstance().getTraceAttributes(traceTypeId);
296 traceBundle = ce.getContributor().getName();
297 traceIcon = ce.getAttribute(TmfTraceType.ICON_ATTR);
298 }
299
300 resource.setPersistentProperty(TmfCommonConstants.TRACEBUNDLE, traceBundle);
301 resource.setPersistentProperty(TmfCommonConstants.TRACETYPE, traceTypeId);
302 resource.setPersistentProperty(TmfCommonConstants.TRACEICON, traceIcon);
303
304 TmfProjectElement tmfProject = TmfProjectRegistry.getProject(resource.getProject(), true);
305 final TmfTraceFolder tracesFolder = tmfProject.getTracesFolder();
306 List<TmfTraceElement> traces = tracesFolder.getTraces();
307 for (TmfTraceElement traceElement : traces) {
308 if (traceElement.getName().equals(resource.getName())) {
309 traceElement.refreshTraceType();
310 break;
311 }
312 }
313 tmfProject.refresh();
314 return Status.OK_STATUS;
315 }
316 }
This page took 0.037528 seconds and 6 git commands to generate.