Copyright header update, 2015 edition
[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 * @since 2.0
66 */
67 public final class TmfTraceManager {
68
69 // ------------------------------------------------------------------------
70 // Attributes
71 // ------------------------------------------------------------------------
72
73 private final Map<ITmfTrace, TmfTraceContext> fTraces;
74
75 /** The currently-selected trace. Should always be part of the trace map */
76 private ITmfTrace fCurrentTrace = null;
77
78 private static final String TEMP_DIR_NAME = ".temp"; //$NON-NLS-1$
79
80 // ------------------------------------------------------------------------
81 // Constructor
82 // ------------------------------------------------------------------------
83
84 private TmfTraceManager() {
85 fTraces = new LinkedHashMap<>();
86 TmfSignalManager.registerVIP(this);
87 }
88
89 /** Singleton instance */
90 private static TmfTraceManager tm = null;
91
92 /**
93 * Get an instance of the trace manager.
94 *
95 * @return The trace manager
96 */
97 public static synchronized TmfTraceManager getInstance() {
98 if (tm == null) {
99 tm = new TmfTraceManager();
100 }
101 return tm;
102 }
103
104 // ------------------------------------------------------------------------
105 // Accessors
106 // ------------------------------------------------------------------------
107
108 /**
109 * @return The begin timestamp of selection
110 * @since 2.1
111 */
112 public ITmfTimestamp getSelectionBeginTime() {
113 return getCurrentTraceContext().getSelectionBegin();
114 }
115
116 /**
117 * @return The end timestamp of selection
118 * @since 2.1
119 */
120 public ITmfTimestamp getSelectionEndTime() {
121 return getCurrentTraceContext().getSelectionEnd();
122 }
123
124 /**
125 * Return the current window time range.
126 *
127 * @return the current window time range
128 */
129 public synchronized TmfTimeRange getCurrentRange() {
130 return getCurrentTraceContext().getWindowRange();
131 }
132
133 /**
134 * Gets the filter applied to the current trace
135 *
136 * @return a filter, or <code>null</code>
137 * @since 2.2
138 */
139 public synchronized ITmfFilter getCurrentFilter() {
140 return getCurrentTraceContext().getFilter();
141 }
142
143 /**
144 * Get the currently selected trace (normally, the focused editor).
145 *
146 * @return The active trace
147 */
148 public synchronized ITmfTrace getActiveTrace() {
149 return fCurrentTrace;
150 }
151
152 /**
153 * Get the trace set of the currently active trace.
154 *
155 * @return The active trace set
156 * @see #getTraceSet(ITmfTrace)
157 */
158 public synchronized @NonNull Collection<ITmfTrace> getActiveTraceSet() {
159 final ITmfTrace trace = fCurrentTrace;
160 return getTraceSet(trace);
161 }
162
163 /**
164 * Get the currently-opened traces, as an unmodifiable set.
165 *
166 * @return A set containing the opened traces
167 */
168 public synchronized Set<ITmfTrace> getOpenedTraces() {
169 return Collections.unmodifiableSet(fTraces.keySet());
170 }
171
172 /**
173 * Get the editor file for an opened trace.
174 *
175 * @param trace
176 * the trace
177 * @return the editor file or null if the trace is not opened
178 * @since 3.0
179 */
180 public synchronized IFile getTraceEditorFile(ITmfTrace trace) {
181 TmfTraceContext ctx = fTraces.get(trace);
182 if (ctx != null) {
183 return ctx.getEditorFile();
184 }
185 return null;
186 }
187
188 private TmfTraceContext getCurrentTraceContext() {
189 TmfTraceContext curCtx = fTraces.get(fCurrentTrace);
190 if (curCtx == null) {
191 /* There are no traces opened at the moment. */
192 return TmfTraceContext.NULL_CONTEXT;
193 }
194 return curCtx;
195 }
196
197 // ------------------------------------------------------------------------
198 // Public utility methods
199 // ------------------------------------------------------------------------
200
201 /**
202 * Get the trace set of a given trace. For a standard trace, this is simply
203 * an array with only that trace in it. For experiments, this is an array of
204 * all the traces contained in this experiment.
205 *
206 * @param trace
207 * The trace or experiment
208 * @return The corresponding trace set.
209 */
210 public static @NonNull Collection<ITmfTrace> getTraceSet(ITmfTrace trace) {
211 if (trace == null) {
212 return NonNullUtils.checkNotNull(ImmutableSet.<ITmfTrace> of());
213 }
214 List<ITmfTrace> traces = trace.getChildren(ITmfTrace.class);
215 if (traces.size() > 0) {
216 return NonNullUtils.checkNotNull(ImmutableSet.copyOf(traces));
217 }
218 return NonNullUtils.checkNotNull(ImmutableSet.of(trace));
219 }
220
221 /**
222 * Get the trace set of a given trace or experiment, including the
223 * experiment. For a standard trace, this is simply a set containing only
224 * that trace. For experiments, it is the set of all the traces contained in
225 * this experiment, along with the experiment.
226 *
227 * @param trace
228 * The trace or experiment
229 * @return The corresponding trace set, including the experiment.
230 * @since 3.1
231 */
232 public static @NonNull Collection<ITmfTrace> getTraceSetWithExperiment(ITmfTrace trace) {
233 if (trace == null) {
234 return checkNotNull(ImmutableSet.<ITmfTrace> of());
235 }
236 if (trace instanceof TmfExperiment) {
237 TmfExperiment exp = (TmfExperiment) trace;
238 List<ITmfTrace> traces = exp.getTraces();
239 Set<ITmfTrace> alltraces = new LinkedHashSet<>(traces);
240 alltraces.add(exp);
241 return NonNullUtils.checkNotNull(ImmutableSet.copyOf(alltraces));
242 }
243 return checkNotNull(Collections.singleton(trace));
244 }
245
246 /**
247 * Return the path (as a string) to the directory for supplementary files to
248 * use with a given trace. If no supplementary file directory has been
249 * configured, a temporary directory based on the trace's name will be
250 * provided.
251 *
252 * @param trace
253 * The trace
254 * @return The path to the supplementary file directory (trailing slash is
255 * INCLUDED!)
256 */
257 public static String getSupplementaryFileDir(ITmfTrace trace) {
258 IResource resource = trace.getResource();
259 if (resource == null) {
260 return getTemporaryDir(trace);
261 }
262
263 String supplDir = null;
264 try {
265 supplDir = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER);
266 } catch (CoreException e) {
267 return getTemporaryDir(trace);
268 }
269 return supplDir + File.separator;
270 }
271
272 /**
273 * Refresh the supplementary files resources for a trace, so it can pick up
274 * new files that got created.
275 *
276 * @param trace
277 * The trace for which to refresh the supplementary files
278 * @since 3.0
279 */
280 public static void refreshSupplementaryFiles(ITmfTrace trace) {
281 IResource resource = trace.getResource();
282 if (resource != null && resource.exists()) {
283 String supplFolderPath = getSupplementaryFileDir(trace);
284 IProject project = resource.getProject();
285 /* Remove the project's path from the supplementary path dir */
286 if (!supplFolderPath.startsWith(project.getLocationURI().getPath())) {
287 Activator.logWarning(String.format("Supplementary files folder for trace %s is not within the project.", trace.getName())); //$NON-NLS-1$
288 return;
289 }
290 IFolder supplFolder = project.getFolder(supplFolderPath.substring(project.getLocationURI().getPath().length()));
291 if (supplFolder.exists()) {
292 try {
293 supplFolder.refreshLocal(IResource.DEPTH_INFINITE, null);
294 } catch (CoreException e) {
295 Activator.logError("Error refreshing resources", e); //$NON-NLS-1$
296 }
297 }
298 }
299 }
300
301 // ------------------------------------------------------------------------
302 // Signal handlers
303 // ------------------------------------------------------------------------
304
305 /**
306 * Signal handler for the traceOpened signal.
307 *
308 * @param signal
309 * The incoming signal
310 */
311 @TmfSignalHandler
312 public synchronized void traceOpened(final TmfTraceOpenedSignal signal) {
313 final ITmfTrace trace = signal.getTrace();
314 final IFile editorFile = signal.getEditorFile();
315 final ITmfTimestamp startTs = trace.getStartTime();
316
317 /* Calculate the initial time range */
318 final int SCALE = ITmfTimestamp.NANOSECOND_SCALE;
319 long offset = trace.getInitialRangeOffset().normalize(0, SCALE).getValue();
320 long endTime = startTs.normalize(0, SCALE).getValue() + offset;
321 final TmfTimeRange startTr = new TmfTimeRange(startTs, new TmfTimestamp(endTime, SCALE));
322
323 final TmfTraceContext startCtx = new TmfTraceContext(startTs, startTs, startTr, editorFile);
324
325 fTraces.put(trace, startCtx);
326
327 /* We also want to set the newly-opened trace as the active trace */
328 fCurrentTrace = trace;
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
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
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) {
371 fTraces.remove(signal.getTrace());
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
385 * selection time range will be updated.
386 *
387 * @param signal
388 * The incoming signal
389 */
390 @TmfSignalHandler
391 public synchronized void timeUpdated(final TmfTimeSynchSignal signal) {
392 final ITmfTimestamp beginTs = signal.getBeginTime();
393 final ITmfTimestamp endTs = signal.getEndTime();
394
395 for (Map.Entry<ITmfTrace, TmfTraceContext> entry : fTraces.entrySet()) {
396 final ITmfTrace trace = entry.getKey();
397 if (beginTs.intersects(getValidTimeRange(trace)) || endTs.intersects(getValidTimeRange(trace))) {
398 TmfTraceContext prevCtx = entry.getValue();
399 TmfTraceContext newCtx = new TmfTraceContext(prevCtx, beginTs, endTs);
400 entry.setValue(newCtx);
401 }
402 }
403 }
404
405 /**
406 * Signal handler for the TmfRangeSynchSignal signal.
407 *
408 * The current window time range of *all* valid traces will be updated to
409 * the new requested times.
410 *
411 * @param signal
412 * The incoming signal
413 */
414 @TmfSignalHandler
415 public synchronized void timeRangeUpdated(final TmfRangeSynchSignal signal) {
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
422 /* Determine the new time range */
423 TmfTimeRange targetTr = signal.getCurrentRange().getIntersection(validTr);
424 TmfTimeRange newTr = (targetTr == null ? curCtx.getWindowRange() : targetTr);
425
426 /* Update the values */
427 TmfTraceContext newCtx = new TmfTraceContext(curCtx, newTr);
428 entry.setValue(newCtx);
429 }
430 }
431
432 // ------------------------------------------------------------------------
433 // Private utility methods
434 // ------------------------------------------------------------------------
435
436 /**
437 * Return the valid time range of a trace (not the current window time
438 * range, but the range of all possible valid timestamps).
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
454 List<ITmfTrace> traces = trace.getChildren(ITmfTrace.class);
455
456 if (traces.isEmpty()) {
457 /* "trace" is a single trace, return its time range directly */
458 return trace.getTimeRange();
459 }
460
461 if (traces.size() == 1) {
462 /* Trace is an experiment with only 1 trace */
463 return traces.get(0).getTimeRange();
464 }
465
466 /*
467 * Trace is an trace set with 2+ traces, so get the earliest start and
468 * the latest end.
469 */
470 ITmfTimestamp start = traces.get(0).getStartTime();
471 ITmfTimestamp end = traces.get(0).getEndTime();
472
473 for (int i = 1; i < traces.size(); i++) {
474 ITmfTrace curTrace = traces.get(i);
475 if (curTrace.getStartTime().compareTo(start) < 0) {
476 start = curTrace.getStartTime();
477 }
478 if (curTrace.getEndTime().compareTo(end) > 0) {
479 end = curTrace.getEndTime();
480 }
481 }
482 return new TmfTimeRange(start, end);
483 }
484
485 /**
486 * Get the temporary directory path. If there is an instance of Eclipse
487 * running, the temporary directory will reside under the workspace.
488 *
489 * @return the temporary directory path suitable to be passed to the
490 * java.io.File constructor without a trailing separator
491 * @since 3.2
492 */
493 public static String getTemporaryDirPath() {
494 // Get the workspace path from the properties
495 String property = System.getProperty("osgi.instance.area"); //$NON-NLS-1$
496 if (property != null) {
497 try {
498 File dir = URIUtil.toFile(URIUtil.fromString(property));
499 dir = new File(dir.getAbsolutePath() + File.separator + TEMP_DIR_NAME);
500 if (!dir.exists()) {
501 dir.mkdirs();
502 }
503 return dir.getAbsolutePath();
504 } catch (URISyntaxException e) {
505 Activator.logError(e.getLocalizedMessage(), e);
506 }
507 }
508 return System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
509 }
510
511 /**
512 * Get a temporary directory based on a trace's name. We will create the
513 * directory if it doesn't exist, so that it's ready to be used.
514 */
515 private static String getTemporaryDir(ITmfTrace trace) {
516 String pathName = getTemporaryDirPath() +
517 File.separator +
518 trace.getName() +
519 File.separator;
520 File dir = new File(pathName);
521 if (!dir.exists()) {
522 dir.mkdirs();
523 }
524 return pathName;
525 }
526 }
This page took 0.045512 seconds and 5 git commands to generate.