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