From: Alexandre Montplaisir Date: Mon, 29 Apr 2013 15:47:15 +0000 (-0400) Subject: tmf: Introduce a central trace manager X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=fc526aefd84e9924904073ea775f3679a3799b4c;p=deliverable%2Ftracecompass.git tmf: Introduce a central trace manager This manager will track all currently opened traces and their currently selected timestamps and time ranges. This will also give both views and *.core analysis plugins a centralized way to get the current active trace/experiment. This was not an easy thing to do reliably if you were not an instance of TmfView. Basic unit tests are included, more complete tests will be added later. Change-Id: Ie0056252908ae8bb9e5da33242c3141665eb161c Signed-off-by: Alexandre Montplaisir Reviewed-on: https://git.eclipse.org/r/12079 Tested-by: Hudson CI Reviewed-by: Bernd Hufmann IP-Clean: Bernd Hufmann --- diff --git a/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/trace/AllTests.java b/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/trace/AllTests.java index 8e280f0e23..52eb7f0000 100644 --- a/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/trace/AllTests.java +++ b/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/trace/AllTests.java @@ -30,6 +30,7 @@ import org.junit.runners.Suite; TmfExperimentTest.class, TmfLocationTest.class, TmfMultiTraceExperimentTest.class, + TmfTraceManagerTest.class, TmfTraceTest.class }) public class AllTests { diff --git a/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/trace/TmfTraceManagerTest.java b/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/trace/TmfTraceManagerTest.java new file mode 100644 index 0000000000..35545ebd03 --- /dev/null +++ b/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/trace/TmfTraceManagerTest.java @@ -0,0 +1,349 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Montplaisir - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.tmf.core.tests.trace; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; + +import org.eclipse.linuxtools.tmf.core.event.ITmfEvent; +import org.eclipse.linuxtools.tmf.core.signal.TmfRangeSynchSignal; +import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager; +import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal; +import org.eclipse.linuxtools.tmf.core.signal.TmfTraceClosedSignal; +import org.eclipse.linuxtools.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.linuxtools.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.linuxtools.tmf.core.tests.shared.CtfTmfTestTraces; +import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp; +import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace; +import org.eclipse.linuxtools.tmf.core.trace.TmfExperiment; +import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Test suite for the {@link TmfTraceManager}. + * + * @author Alexandre Montplaisir + */ +public class TmfTraceManagerTest { + + private static final int SCALE = ITmfTimestamp.NANOSECOND_SCALE; + + private static ITmfTrace trace1; + private static final long t1start = 1331668247314038062L; + private static final long t1end = 1331668259054285979L; + + private static ITmfTrace trace2; + private static final long t2start = 1332170682440133097L; + private static final long t2end = 1332170692664579801L; + + private static final long ONE_SECOND = 1000000000L; + + private TmfTraceManager tm; + + + /** + * Test class initialization + */ + @BeforeClass + public static void setUpClass() { + assumeTrue(CtfTmfTestTraces.tracesExist()); + trace1 = CtfTmfTestTraces.getTestTrace(1); + trace2 = CtfTmfTestTraces.getTestTrace(0); + + trace1.indexTrace(true); + trace2.indexTrace(true); + } + + /** + * Test initialization + */ + @Before + public void setUp() { + tm = TmfTraceManager.getInstance(); + } + + /** + * Test clean-up + */ + @After + public void tearDown() { + while (tm.getActiveTrace() != null) { + closeTrace(tm.getActiveTrace()); + } + } + + // ------------------------------------------------------------------------ + // Dummy actions (fake signals) + // ------------------------------------------------------------------------ + + private void openTrace(ITmfTrace trace) { + TmfSignalManager.dispatchSignal(new TmfTraceOpenedSignal(this, trace, null)); + selectTrace(trace); + } + + private void closeTrace(ITmfTrace trace) { + TmfSignalManager.dispatchSignal(new TmfTraceClosedSignal(this, trace)); + } + + private void selectTrace(ITmfTrace trace) { + TmfSignalManager.dispatchSignal(new TmfTraceSelectedSignal(this, trace)); + } + + private void selectTimestamp(ITmfTimestamp ts) { + TmfSignalManager.dispatchSignal(new TmfTimeSynchSignal(this, ts)); + } + + private void selectTimeRange(TmfTimeRange tr) { + TmfSignalManager.dispatchSignal(new TmfRangeSynchSignal(this, tr, null)); + } + + // ------------------------------------------------------------------------ + // General tests + // ------------------------------------------------------------------------ + + /** + * Test that the manager is correctly initialized + */ + @Test + public void testInitialize() { + TmfTraceManager mgr = TmfTraceManager.getInstance(); + assertNotNull(mgr); + } + + // ------------------------------------------------------------------------ + // Test a single trace + // ------------------------------------------------------------------------ + + /** + * Test the initial range of a single trace. + */ + @Test + public void testTraceInitialRange() { + openTrace(trace2); + final TmfTimeRange expectedRange = new TmfTimeRange( + trace2.getStartTime(), + calculateOffset(trace2.getStartTime(), trace2.getInitialRangeOffset())); + TmfTimeRange actualRange = tm.getCurrentRange(); + assertEquals(expectedRange, actualRange); + } + + /** + * Try selecting a timestamp contained inside the trace's range. The trace's + * current time should get updated correctly. + */ + @Test + public void testNewTimestamp() { + openTrace(trace2); + ITmfTimestamp ts = new TmfTimestamp(t2start + ONE_SECOND, SCALE); + selectTimestamp(ts); + + ITmfTimestamp afterTs = tm.getCurrentTime(); + assertEquals(ts, afterTs); + } + + /** + * Try selecting a timestamp happening before the trace's start. The change + * should be ignored. + */ + @Test + public void testTimestampBefore() { + openTrace(trace2); + ITmfTimestamp beforeTs = tm.getCurrentTime(); + ITmfTimestamp ts = new TmfTimestamp(t2start - ONE_SECOND, SCALE); + selectTimestamp(ts); + + ITmfTimestamp curTs = tm.getCurrentTime(); + assertEquals(beforeTs, curTs); + } + + /** + * Try selecting a timestamp happening after the trace's end. The change + * should be ignored. + */ + @Test + public void testTimestampAfter() { + openTrace(trace2); + ITmfTimestamp beforeTs = tm.getCurrentTime(); + ITmfTimestamp ts = new TmfTimestamp(t2end + ONE_SECOND, SCALE); + selectTimestamp(ts); + + ITmfTimestamp curTs = tm.getCurrentTime(); + assertEquals(beforeTs, curTs); + } + + /** + * Test selecting a normal sub-range of a single trace. + */ + @Test + public void testTraceNewTimeRange() { + openTrace(trace2); + TmfTimeRange range = new TmfTimeRange( + new TmfTimestamp(t2start + ONE_SECOND, SCALE), + new TmfTimestamp(t2end - ONE_SECOND, SCALE)); + selectTimeRange(range); + + TmfTimeRange curRange = tm.getCurrentRange(); + assertEquals(range.getStartTime(), curRange.getStartTime()); + assertEquals(range.getEndTime(), curRange.getEndTime()); + } + + /** + * Test selecting a range whose start time is before the trace's start time. + * The selected range should get clamped to the trace's range. + */ + @Test + public void testTraceTimeRangeClampingStart() { + openTrace(trace2); + TmfTimeRange range = new TmfTimeRange( + new TmfTimestamp(t2start - ONE_SECOND, SCALE), // minus here + new TmfTimestamp(t2end - ONE_SECOND, SCALE)); + selectTimeRange(range); + + TmfTimeRange curRange = tm.getCurrentRange(); + assertEquals(t2start, curRange.getStartTime().getValue()); + assertEquals(range.getEndTime(), curRange.getEndTime()); + } + + /** + * Test selecting a range whose end time is after the trace's end time. + * The selected range should get clamped to the trace's range. + */ + @Test + public void testTraceTimeRangeClampingEnd() { + openTrace(trace2); + TmfTimeRange range = new TmfTimeRange( + new TmfTimestamp(t2start + ONE_SECOND, SCALE), + new TmfTimestamp(t2end + ONE_SECOND, SCALE)); // plus here + selectTimeRange(range); + + TmfTimeRange curRange = tm.getCurrentRange(); + assertEquals(range.getStartTime(), curRange.getStartTime()); + assertEquals(t2end, curRange.getEndTime().getValue()); + } + + /** + * Test selecting a range whose both start and end times are outside of the + * trace's range. The selected range should get clamped to the trace's + * range. + */ + @Test + public void testTraceTimeRangeClampingBoth() { + openTrace(trace2); + TmfTimeRange range = new TmfTimeRange( + new TmfTimestamp(t2start - ONE_SECOND, SCALE), // minus here + new TmfTimestamp(t2end + ONE_SECOND, SCALE)); // plus here + selectTimeRange(range); + + TmfTimeRange curRange = tm.getCurrentRange(); + assertEquals(t2start, curRange.getStartTime().getValue()); + assertEquals(t2end, curRange.getEndTime().getValue()); + } + + // ------------------------------------------------------------------------ + // Test multiple traces in parallel + // ------------------------------------------------------------------------ + + // ------------------------------------------------------------------------ + // Test an experiment + // ------------------------------------------------------------------------ + + /** + * Test the initial range of an experiment. + */ + @Test + public void testExperimentInitialRange() { + TmfExperiment exp = createExperiment(trace1, trace2); + openTrace(exp); + /* + * The initial range should be == to the initial range of the earliest + * trace (here trace1). + */ + final TmfTimeRange traceInitialRange = new TmfTimeRange( + trace1.getStartTime(), + calculateOffset(trace1.getStartTime(), trace1.getInitialRangeOffset())); + + final TmfTimeRange expInitialRange = new TmfTimeRange( + exp.getStartTime(), + calculateOffset(exp.getStartTime(), exp.getInitialRangeOffset())); + + final TmfTimeRange actualRange = tm.getCurrentRange(); + + assertEquals(traceInitialRange, actualRange); + assertEquals(expInitialRange, actualRange); + } + + /** + * Test the range clamping when both the start and end times of the signal's + * range are outside of the trace's range. The range should clamp to the + * experiment's range. + */ + @Test + public void testExperimentRangeClampingBoth() { + TmfExperiment exp = createExperiment(trace1, trace2); + openTrace(exp); + + final TmfTimeRange range = new TmfTimeRange( + new TmfTimestamp(t1start - ONE_SECOND, SCALE), + new TmfTimestamp(t2end + ONE_SECOND, SCALE)); + selectTimeRange(range); + + TmfTimeRange actualRange = tm.getCurrentRange(); + assertEquals(t1start, actualRange.getStartTime().getValue()); + assertEquals(t2end, actualRange.getEndTime().getValue()); + } + + /** + * Test selecting a range in-between two disjoint traces in an experiment. + * The range should still get correctly selected, even if no trace has any + * events in that range. + */ + @Test + public void testExperimentRangeInBetween() { + TmfExperiment exp = createExperiment(trace1, trace2); + openTrace(exp); + + final TmfTimeRange range = new TmfTimeRange( + new TmfTimestamp(t1end + ONE_SECOND, SCALE), + new TmfTimestamp(t2start - ONE_SECOND, SCALE)); + selectTimeRange(range); + + TmfTimeRange actualRange = tm.getCurrentRange(); + assertEquals(range, actualRange); + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + private static TmfExperiment createExperiment(ITmfTrace t1, ITmfTrace t2) { + ITmfTrace[] traces = new ITmfTrace[] { t1, t2 }; + TmfExperiment exp = new TmfExperiment(ITmfEvent.class, "test-exp", traces); + exp.indexTrace(true); + return exp; + } + + /** + * Basically a "initial + offset" operation, but for ITmfTimetamp objects. + */ + private static ITmfTimestamp calculateOffset(ITmfTimestamp initialTs, ITmfTimestamp offsetTs) { + long start = initialTs.normalize(0, SCALE).getValue(); + long offset = offsetTs.normalize(0, SCALE).getValue(); + return new TmfTimestamp(start + offset, SCALE); + } +} diff --git a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/Activator.java b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/Activator.java index 0e8a46f0e6..f2f2b95df3 100644 --- a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/Activator.java +++ b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/internal/tmf/core/Activator.java @@ -15,6 +15,7 @@ package org.eclipse.linuxtools.internal.tmf.core; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Plugin; import org.eclipse.core.runtime.Status; +import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager; import org.osgi.framework.BundleContext; /** @@ -78,6 +79,8 @@ public class Activator extends Plugin { super.start(context); setDefault(this); TmfCoreTracer.init(); + /* Initialize the trace manager */ + TmfTraceManager.getInstance(); } @Override diff --git a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/trace/TmfTraceContext.java b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/trace/TmfTraceContext.java new file mode 100644 index 0000000000..29e895a6db --- /dev/null +++ b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/trace/TmfTraceContext.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Montplaisir - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.tmf.core.trace; + +import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp; + +/** + * Context of a trace, which is the representation of the "view" the user + * currently has on this trace (selected time range, selected time stamp). + * + * TODO could be extended to support the notion of current location too. + * + * @author Alexandre Montplaisir + * @since 2.0 + */ +final class TmfTraceContext { + + static final TmfTraceContext NULL_CONTEXT = + new TmfTraceContext(TmfTimestamp.BIG_CRUNCH, TmfTimeRange.NULL_RANGE); + + private final ITmfTimestamp fTimestamp; + private final TmfTimeRange fTimerange; + + public TmfTraceContext(ITmfTimestamp ts, TmfTimeRange tr) { + fTimestamp = ts; + fTimerange = tr; + } + + public TmfTraceContext(TmfTraceContext prevCtx, ITmfTimestamp ts) { + fTimestamp = ts; + fTimerange = prevCtx.fTimerange; + } + + public ITmfTimestamp getTimestamp() { + return fTimestamp; + } + + public TmfTimeRange getTimerange() { + return fTimerange; + } + + public boolean isValid() { + if (fTimestamp.compareTo(TmfTimestamp.ZERO) <= 0 || + fTimerange.getEndTime().compareTo(fTimerange.getStartTime()) <= 0) { + return false; + } + return true; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[fTimestamp=" + fTimestamp.toString() + //$NON-NLS-1$ + ", fTimerange=" + fTimerange + ']'; //$NON-NLS-1$ + } +} diff --git a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/trace/TmfTraceManager.java b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/trace/TmfTraceManager.java new file mode 100644 index 0000000000..d524b8fed1 --- /dev/null +++ b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/trace/TmfTraceManager.java @@ -0,0 +1,308 @@ +/******************************************************************************* + * Copyright (c) 2013 Ericsson + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Alexandre Montplaisir - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.linuxtools.tmf.core.trace; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.linuxtools.tmf.core.signal.TmfRangeSynchSignal; +import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler; +import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager; +import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal; +import org.eclipse.linuxtools.tmf.core.signal.TmfTraceClosedSignal; +import org.eclipse.linuxtools.tmf.core.signal.TmfTraceOpenedSignal; +import org.eclipse.linuxtools.tmf.core.signal.TmfTraceSelectedSignal; +import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp; +import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange; +import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp; + +/** + * Central trace manager for TMF. It tracks the currently opened traces and + * experiment, as well as the currently-selected timestamps and time ranges for + * each one of those. + * + * It's a singleton class, so only one instance should exist (available via + * {@link #getInstance()}). + * + * @author Alexandre Montplaisir + * @since 2.0 + */ +public final class TmfTraceManager { + + // ------------------------------------------------------------------------ + // Attributes + // ------------------------------------------------------------------------ + + private final Map fTraces; + + /** The currently-selected trace. Should always be part of the trace map */ + private ITmfTrace fCurrentTrace = null; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + private TmfTraceManager() { + fTraces = new LinkedHashMap(); + TmfSignalManager.registerVIP(this); + } + + /** Singleton instance */ + private static TmfTraceManager tm = null; + + /** + * Get an instance of the trace manager. + * + * @return The trace manager + */ + public static synchronized TmfTraceManager getInstance() { + if (tm == null) { + tm = new TmfTraceManager(); + } + return tm; + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Return the current selected time. + * + * @return the current time stamp + */ + public synchronized ITmfTimestamp getCurrentTime() { + return getCurrentTraceContext().getTimestamp(); + } + + /** + * Return the current selected range. + * + * @return the current time range + */ + public synchronized TmfTimeRange getCurrentRange() { + return getCurrentTraceContext().getTimerange(); + } + + /** + * Get the currently selected trace (normally, the focused editor). + * + * @return The active trace + */ + public synchronized ITmfTrace getActiveTrace() { + return fCurrentTrace; + } + + /** + * Get the currently active trace set. For a 'normal' trace, this is simply + * an array with only that trace in it. For trace experiments, this will be + * an array containing the 'real' child traces in the experiment. + * + * @return The active trace set + */ + public synchronized ITmfTrace[] getActiveTraceSet() { + final ITmfTrace trace = fCurrentTrace; + if (trace instanceof TmfExperiment) { + final TmfExperiment exp = (TmfExperiment) trace; + return exp.getTraces(); + } + return new ITmfTrace[] { trace }; + } + + private TmfTraceContext getCurrentTraceContext() { + TmfTraceContext curCtx = fTraces.get(fCurrentTrace); + if (curCtx == null) { + /* There are no traces opened at the moment. */ + return TmfTraceContext.NULL_CONTEXT; + } + return curCtx; + } + + // ------------------------------------------------------------------------ + // Signal handlers + // ------------------------------------------------------------------------ + + /** + * Signal handler for the traceOpened signal. + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public synchronized void traceOpened(final TmfTraceOpenedSignal signal) { + final ITmfTrace trace = signal.getTrace(); + final ITmfTimestamp startTs = trace.getStartTime(); + + /* Calculate the initial time range */ + final int SCALE = ITmfTimestamp.NANOSECOND_SCALE; + long offset = trace.getInitialRangeOffset().normalize(0, SCALE).getValue(); + long endTime = startTs.normalize(0, SCALE).getValue() + offset; + final TmfTimeRange startTr = new TmfTimeRange(startTs, new TmfTimestamp(endTime, SCALE)); + + final TmfTraceContext startCtx = new TmfTraceContext(startTs, startTr); + + fTraces.put(trace, startCtx); + + /* We also want to set the newly-opened trace as the active trace */ + fCurrentTrace = trace; + } + + + /** + * Handler for the TmfTraceSelectedSignal. + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public synchronized void traceSelected(final TmfTraceSelectedSignal signal) { + final ITmfTrace newTrace = signal.getTrace(); + if (!fTraces.containsKey(newTrace)) { + throw new RuntimeException(); + } + fCurrentTrace = newTrace; + } + + /** + * Signal handler for the traceClosed signal. + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public synchronized void traceClosed(final TmfTraceClosedSignal signal) { + TmfTraceContext ret = fTraces.remove(signal.getTrace()); + if (ret == null) { + throw new RuntimeException(); + } + if (fTraces.size() == 0) { + fCurrentTrace = null; + /* + * In other cases, we should receive a traceSelected signal that + * will indicate which trace is the new one. + */ + } + } + + /** + * Signal handler for the TmfTimeSynchSignal signal. + * + * The current time of *all* traces whose range contains the requested new + * time will be updated. + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public synchronized void timeUpdated(final TmfTimeSynchSignal signal) { + final ITmfTimestamp ts = signal.getCurrentTime(); + + for (Map.Entry entry : fTraces.entrySet()) { + final ITmfTrace trace = entry.getKey(); + if (ts.intersects(getValidTimeRange(trace))) { + TmfTraceContext prevCtx = entry.getValue(); + TmfTraceContext newCtx = new TmfTraceContext(prevCtx, ts); + entry.setValue(newCtx); + } + } + } + + /** + * Signal handler for the TmfRangeSynchSignal signal. + * + * The current timestamp and timerange of *all* valid traces will be updated + * to the new requested times. + * + * @param signal + * The incoming signal + */ + @TmfSignalHandler + public synchronized void timeRangeUpdated(final TmfRangeSynchSignal signal) { + final ITmfTimestamp signalTs = signal.getCurrentTime(); + + for (Map.Entry entry : fTraces.entrySet()) { + final ITmfTrace trace = entry.getKey(); + final TmfTraceContext curCtx = entry.getValue(); + + final TmfTimeRange validTr = getValidTimeRange(trace); + + /* Determine the new time stamp */ + ITmfTimestamp newTs; + if (signalTs != null && signalTs.intersects(validTr)) { + newTs = signalTs; + } else { + newTs = curCtx.getTimestamp(); + } + + /* Determine the new time range */ + TmfTimeRange targetTr = signal.getCurrentRange().getIntersection(validTr); + TmfTimeRange newTr = (targetTr == null ? curCtx.getTimerange() : targetTr); + + /* Update the values */ + TmfTraceContext newCtx = new TmfTraceContext(newTs, newTr); + entry.setValue(newCtx); + } + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Return the valid time range of a trace (not the "current time range", but + * the range of all possible valid timestamps). + * + * For a real trace this is the whole range of the trace. For an experiment, + * it goes from the start time of the earliest trace to the end time of the + * latest one. + * + * @param trace + * The trace to check for + * @return The valid time span, or 'null' if the trace is not valid + */ + private TmfTimeRange getValidTimeRange(ITmfTrace trace) { + if (!fTraces.containsKey(trace)) { + /* Trace is not part of the currently opened traces */ + return null; + } + if (!(trace instanceof TmfExperiment)) { + /* "trace" is a single trace, return its time range directly */ + return trace.getTimeRange(); + } + final ITmfTrace[] traces = ((TmfExperiment) trace).getTraces(); + if (traces.length == 0) { + /* We are being trolled */ + return null; + } + if (traces.length == 1) { + /* Trace is an experiment with only 1 trace */ + return traces[0].getTimeRange(); + } + /* + * Trace is an experiment with 2+ traces, so get the earliest start and + * the latest end. + */ + ITmfTimestamp start = traces[0].getStartTime(); + ITmfTimestamp end = traces[0].getEndTime(); + for (int i = 1; i < traces.length; i++) { + ITmfTrace curTrace = traces[i]; + if (curTrace.getStartTime().compareTo(start) < 0) { + start = curTrace.getStartTime(); + } + if (curTrace.getEndTime().compareTo(end) > 0) { + end = curTrace.getEndTime(); + } + } + return new TmfTimeRange(start, end); + } +}