b471099fccc9cec1c8b82b9feae46717b4be8a72
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / TmfTraceManager.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 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 - Support selection range
12 * Xavier Raynaud - Support filters tracking
13 *******************************************************************************/
14
15 package org.eclipse.tracecompass.tmf.core.trace;
16
17 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
18
19 import java.io.File;
20 import java.net.URISyntaxException;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.LinkedHashMap;
24 import java.util.LinkedHashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 import org.eclipse.core.resources.IFile;
30 import org.eclipse.core.resources.IFolder;
31 import org.eclipse.core.resources.IProject;
32 import org.eclipse.core.resources.IResource;
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.core.runtime.URIUtil;
35 import org.eclipse.jdt.annotation.NonNull;
36 import org.eclipse.tracecompass.common.core.NonNullUtils;
37 import org.eclipse.tracecompass.internal.tmf.core.Activator;
38 import org.eclipse.tracecompass.tmf.core.TmfCommonConstants;
39 import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfEventFilterAppliedSignal;
41 import org.eclipse.tracecompass.tmf.core.signal.TmfRangeSynchSignal;
42 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
43 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
44 import org.eclipse.tracecompass.tmf.core.signal.TmfTimeSynchSignal;
45 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
46 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
47 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
48 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
49 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
50 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
51 import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;
52
53 import com.google.common.collect.ImmutableSet;
54
55 /**
56 * Central trace manager for TMF. It tracks the currently opened traces and
57 * experiment, as well as the currently-selected time or time range and the
58 * current window time range for each one of those. It also tracks filters
59 * applied for each trace.
60 *
61 * It's a singleton class, so only one instance should exist (available via
62 * {@link #getInstance()}).
63 *
64 * @author Alexandre Montplaisir
65 */
66 public final class TmfTraceManager {
67
68 // ------------------------------------------------------------------------
69 // Attributes
70 // ------------------------------------------------------------------------
71
72 private final Map<ITmfTrace, TmfTraceContext> fTraces;
73
74 /** The currently-selected trace. Should always be part of the trace map */
75 private ITmfTrace fCurrentTrace = null;
76
77 private static final String TEMP_DIR_NAME = ".temp"; //$NON-NLS-1$
78
79 // ------------------------------------------------------------------------
80 // Constructor
81 // ------------------------------------------------------------------------
82
83 private TmfTraceManager() {
84 fTraces = new LinkedHashMap<>();
85 TmfSignalManager.registerVIP(this);
86 }
87
88 /** Singleton instance */
89 private static TmfTraceManager tm = null;
90
91 /**
92 * Get an instance of the trace manager.
93 *
94 * @return The trace manager
95 */
96 public static synchronized TmfTraceManager getInstance() {
97 if (tm == null) {
98 tm = new TmfTraceManager();
99 }
100 return tm;
101 }
102
103 // ------------------------------------------------------------------------
104 // Accessors
105 // ------------------------------------------------------------------------
106
107 /**
108 * @return The begin timestamp of selection
109 */
110 public ITmfTimestamp getSelectionBeginTime() {
111 return getCurrentTraceContext().getSelectionBegin();
112 }
113
114 /**
115 * @return The end timestamp of selection
116 */
117 public ITmfTimestamp getSelectionEndTime() {
118 return getCurrentTraceContext().getSelectionEnd();
119 }
120
121 /**
122 * Return the current window time range.
123 *
124 * @return the current window time range
125 */
126 public synchronized TmfTimeRange getCurrentRange() {
127 return getCurrentTraceContext().getWindowRange();
128 }
129
130 /**
131 * Gets the filter applied to the current trace
132 *
133 * @return a filter, or <code>null</code>
134 */
135 public synchronized ITmfFilter getCurrentFilter() {
136 return getCurrentTraceContext().getFilter();
137 }
138
139 /**
140 * Get the currently selected trace (normally, the focused editor).
141 *
142 * @return The active trace
143 */
144 public synchronized ITmfTrace getActiveTrace() {
145 return fCurrentTrace;
146 }
147
148 /**
149 * Get the trace set of the currently active trace.
150 *
151 * @return The active trace set
152 * @see #getTraceSet(ITmfTrace)
153 */
154 public synchronized @NonNull Collection<ITmfTrace> getActiveTraceSet() {
155 final ITmfTrace trace = fCurrentTrace;
156 return getTraceSet(trace);
157 }
158
159 /**
160 * Get the currently-opened traces, as an unmodifiable set.
161 *
162 * @return A set containing the opened traces
163 */
164 public synchronized Set<ITmfTrace> getOpenedTraces() {
165 return Collections.unmodifiableSet(fTraces.keySet());
166 }
167
168 /**
169 * Get the editor file for an opened trace.
170 *
171 * @param trace
172 * the trace
173 * @return the editor file or null if the trace is not opened
174 */
175 public synchronized IFile getTraceEditorFile(ITmfTrace trace) {
176 TmfTraceContext ctx = fTraces.get(trace);
177 if (ctx != null) {
178 return ctx.getEditorFile();
179 }
180 return null;
181 }
182
183 private TmfTraceContext getCurrentTraceContext() {
184 TmfTraceContext curCtx = fTraces.get(fCurrentTrace);
185 if (curCtx == null) {
186 /* There are no traces opened at the moment. */
187 return TmfTraceContext.NULL_CONTEXT;
188 }
189 return curCtx;
190 }
191
192 // ------------------------------------------------------------------------
193 // Public utility methods
194 // ------------------------------------------------------------------------
195
196 /**
197 * Get the trace set of a given trace. For a standard trace, this is simply
198 * an array with only that trace in it. For experiments, this is an array of
199 * all the traces contained in this experiment.
200 *
201 * @param trace
202 * The trace or experiment
203 * @return The corresponding trace set.
204 */
205 public static @NonNull Collection<ITmfTrace> getTraceSet(ITmfTrace trace) {
206 if (trace == null) {
207 return NonNullUtils.checkNotNull(ImmutableSet.<ITmfTrace> of());
208 }
209 List<ITmfTrace> traces = trace.getChildren(ITmfTrace.class);
210 if (traces.size() > 0) {
211 return NonNullUtils.checkNotNull(ImmutableSet.copyOf(traces));
212 }
213 return NonNullUtils.checkNotNull(ImmutableSet.of(trace));
214 }
215
216 /**
217 * Get the trace set of a given trace or experiment, including the
218 * experiment. For a standard trace, this is simply a set containing only
219 * that trace. For experiments, it is the set of all the traces contained in
220 * this experiment, along with the experiment.
221 *
222 * @param trace
223 * The trace or experiment
224 * @return The corresponding trace set, including the experiment.
225 */
226 public static @NonNull Collection<ITmfTrace> getTraceSetWithExperiment(ITmfTrace trace) {
227 if (trace == null) {
228 return checkNotNull(ImmutableSet.<ITmfTrace> of());
229 }
230 if (trace instanceof TmfExperiment) {
231 TmfExperiment exp = (TmfExperiment) trace;
232 List<ITmfTrace> traces = exp.getTraces();
233 Set<ITmfTrace> alltraces = new LinkedHashSet<>(traces);
234 alltraces.add(exp);
235 return NonNullUtils.checkNotNull(ImmutableSet.copyOf(alltraces));
236 }
237 return checkNotNull(Collections.singleton(trace));
238 }
239
240 /**
241 * Return the path (as a string) to the directory for supplementary files to
242 * use with a given trace. If no supplementary file directory has been
243 * configured, a temporary directory based on the trace's name will be
244 * provided.
245 *
246 * @param trace
247 * The trace
248 * @return The path to the supplementary file directory (trailing slash is
249 * INCLUDED!)
250 */
251 public static String getSupplementaryFileDir(ITmfTrace trace) {
252 IResource resource = trace.getResource();
253 if (resource == null) {
254 return getTemporaryDir(trace);
255 }
256
257 String supplDir = null;
258 try {
259 supplDir = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER);
260 } catch (CoreException e) {
261 return getTemporaryDir(trace);
262 }
263 return supplDir + File.separator;
264 }
265
266 /**
267 * Refresh the supplementary files resources for a trace, so it can pick up
268 * new files that got created.
269 *
270 * @param trace
271 * The trace for which to refresh the supplementary files
272 */
273 public static void refreshSupplementaryFiles(ITmfTrace trace) {
274 IResource resource = trace.getResource();
275 if (resource != null && resource.exists()) {
276 String supplFolderPath = getSupplementaryFileDir(trace);
277 IProject project = resource.getProject();
278 /* Remove the project's path from the supplementary path dir */
279 if (!supplFolderPath.startsWith(project.getLocationURI().getPath())) {
280 Activator.logWarning(String.format("Supplementary files folder for trace %s is not within the project.", trace.getName())); //$NON-NLS-1$
281 return;
282 }
283 IFolder supplFolder = project.getFolder(supplFolderPath.substring(project.getLocationURI().getPath().length()));
284 if (supplFolder.exists()) {
285 try {
286 supplFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
287 } catch (CoreException e) {
288 Activator.logError("Error refreshing resources", e); //$NON-NLS-1$
289 }
290 }
291 }
292 }
293
294 // ------------------------------------------------------------------------
295 // Signal handlers
296 // ------------------------------------------------------------------------
297
298 /**
299 * Signal handler for the traceOpened signal.
300 *
301 * @param signal
302 * The incoming signal
303 */
304 @TmfSignalHandler
305 public synchronized void traceOpened(final TmfTraceOpenedSignal signal) {
306 final ITmfTrace trace = signal.getTrace();
307 final IFile editorFile = signal.getEditorFile();
308 final ITmfTimestamp startTs = trace.getStartTime();
309
310 /* Calculate the initial time range */
311 final int SCALE = ITmfTimestamp.NANOSECOND_SCALE;
312 long offset = trace.getInitialRangeOffset().normalize(0, SCALE).getValue();
313 long endTime = startTs.normalize(0, SCALE).getValue() + offset;
314 final TmfTimeRange startTr = new TmfTimeRange(startTs, new TmfTimestamp(endTime, SCALE));
315
316 final TmfTraceContext startCtx = new TmfTraceContext(startTs, startTs, startTr, editorFile);
317
318 fTraces.put(trace, startCtx);
319
320 /* We also want to set the newly-opened trace as the active trace */
321 fCurrentTrace = trace;
322 }
323
324 /**
325 * Handler for the TmfTraceSelectedSignal.
326 *
327 * @param signal
328 * The incoming signal
329 */
330 @TmfSignalHandler
331 public synchronized void traceSelected(final TmfTraceSelectedSignal signal) {
332 final ITmfTrace newTrace = signal.getTrace();
333 if (!fTraces.containsKey(newTrace)) {
334 throw new RuntimeException();
335 }
336 fCurrentTrace = newTrace;
337 }
338
339 /**
340 * Signal handler for the filterApplied signal.
341 *
342 * @param signal
343 * The incoming signal
344 */
345 @TmfSignalHandler
346 public synchronized void filterApplied(TmfEventFilterAppliedSignal signal) {
347 final ITmfTrace newTrace = signal.getTrace();
348 TmfTraceContext context = fTraces.get(newTrace);
349 if (context == null) {
350 throw new RuntimeException();
351 }
352 fTraces.put(newTrace, new TmfTraceContext(context, signal.getEventFilter()));
353 }
354
355 /**
356 * Signal handler for the traceClosed signal.
357 *
358 * @param signal
359 * The incoming signal
360 */
361 @TmfSignalHandler
362 public synchronized void traceClosed(final TmfTraceClosedSignal signal) {
363 fTraces.remove(signal.getTrace());
364 if (fTraces.size() == 0) {
365 fCurrentTrace = null;
366 /*
367 * In other cases, we should receive a traceSelected signal that
368 * will indicate which trace is the new one.
369 */
370 }
371 }
372
373 /**
374 * Signal handler for the TmfTimeSynchSignal signal.
375 *
376 * The current time of *all* traces whose range contains the requested new
377 * selection time range will be updated.
378 *
379 * @param signal
380 * The incoming signal
381 */
382 @TmfSignalHandler
383 public synchronized void timeUpdated(final TmfTimeSynchSignal signal) {
384 final ITmfTimestamp beginTs = signal.getBeginTime();
385 final ITmfTimestamp endTs = signal.getEndTime();
386
387 for (Map.Entry<ITmfTrace, TmfTraceContext> entry : fTraces.entrySet()) {
388 final ITmfTrace trace = entry.getKey();
389 if (beginTs.intersects(getValidTimeRange(trace)) || endTs.intersects(getValidTimeRange(trace))) {
390 TmfTraceContext prevCtx = entry.getValue();
391 TmfTraceContext newCtx = new TmfTraceContext(prevCtx, beginTs, endTs);
392 entry.setValue(newCtx);
393 }
394 }
395 }
396
397 /**
398 * Signal handler for the TmfRangeSynchSignal signal.
399 *
400 * The current window time range of *all* valid traces will be updated to
401 * the new requested times.
402 *
403 * @param signal
404 * The incoming signal
405 */
406 @TmfSignalHandler
407 public synchronized void timeRangeUpdated(final TmfRangeSynchSignal signal) {
408 for (Map.Entry<ITmfTrace, TmfTraceContext> entry : fTraces.entrySet()) {
409 final ITmfTrace trace = entry.getKey();
410 final TmfTraceContext curCtx = entry.getValue();
411
412 final TmfTimeRange validTr = getValidTimeRange(trace);
413
414 /* Determine the new time range */
415 TmfTimeRange targetTr = signal.getCurrentRange().getIntersection(validTr);
416 TmfTimeRange newTr = (targetTr == null ? curCtx.getWindowRange() : targetTr);
417
418 /* Update the values */
419 TmfTraceContext newCtx = new TmfTraceContext(curCtx, newTr);
420 entry.setValue(newCtx);
421 }
422 }
423
424 // ------------------------------------------------------------------------
425 // Private utility methods
426 // ------------------------------------------------------------------------
427
428 /**
429 * Return the valid time range of a trace (not the current window time
430 * range, but the range of all possible valid timestamps).
431 *
432 * For a real trace this is the whole range of the trace. For an experiment,
433 * it goes from the start time of the earliest trace to the end time of the
434 * latest one.
435 *
436 * @param trace
437 * The trace to check for
438 * @return The valid time span, or 'null' if the trace is not valid
439 */
440 private TmfTimeRange getValidTimeRange(ITmfTrace trace) {
441 if (!fTraces.containsKey(trace)) {
442 /* Trace is not part of the currently opened traces */
443 return null;
444 }
445
446 List<ITmfTrace> traces = trace.getChildren(ITmfTrace.class);
447
448 if (traces.isEmpty()) {
449 /* "trace" is a single trace, return its time range directly */
450 return trace.getTimeRange();
451 }
452
453 if (traces.size() == 1) {
454 /* Trace is an experiment with only 1 trace */
455 return traces.get(0).getTimeRange();
456 }
457
458 /*
459 * Trace is an trace set with 2+ traces, so get the earliest start and
460 * the latest end.
461 */
462 ITmfTimestamp start = traces.get(0).getStartTime();
463 ITmfTimestamp end = traces.get(0).getEndTime();
464
465 for (int i = 1; i < traces.size(); i++) {
466 ITmfTrace curTrace = traces.get(i);
467 if (curTrace.getStartTime().compareTo(start) < 0) {
468 start = curTrace.getStartTime();
469 }
470 if (curTrace.getEndTime().compareTo(end) > 0) {
471 end = curTrace.getEndTime();
472 }
473 }
474 return new TmfTimeRange(start, end);
475 }
476
477 /**
478 * Get the temporary directory path. If there is an instance of Eclipse
479 * running, the temporary directory will reside under the workspace.
480 *
481 * @return the temporary directory path suitable to be passed to the
482 * java.io.File constructor without a trailing separator
483 */
484 public static String getTemporaryDirPath() {
485 // Get the workspace path from the properties
486 String property = System.getProperty("osgi.instance.area"); //$NON-NLS-1$
487 if (property != null) {
488 try {
489 File dir = URIUtil.toFile(URIUtil.fromString(property));
490 dir = new File(dir.getAbsolutePath() + File.separator + TEMP_DIR_NAME);
491 if (!dir.exists()) {
492 dir.mkdirs();
493 }
494 return dir.getAbsolutePath();
495 } catch (URISyntaxException e) {
496 Activator.logError(e.getLocalizedMessage(), e);
497 }
498 }
499 return System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
500 }
501
502 /**
503 * Get a temporary directory based on a trace's name. We will create the
504 * directory if it doesn't exist, so that it's ready to be used.
505 */
506 private static String getTemporaryDir(ITmfTrace trace) {
507 String pathName = getTemporaryDirPath() +
508 File.separator +
509 trace.getName() +
510 File.separator;
511 File dir = new File(pathName);
512 if (!dir.exists()) {
513 dir.mkdirs();
514 }
515 return pathName;
516 }
517 }
This page took 0.042598 seconds and 5 git commands to generate.