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