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