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