timing.core: Search in segment store table data provider

In the current segment store table data provider when searching for
segments by applying some search filter, the fetched segments that
matches the filter criteria were not highlighted, this change highlights
the segments that are matching the search filter so finding the desired
segment will be easier. Also, it supports seeking to the next or
previous matching segment when a search direction is provided.

Change-Id: I8e14e7204833143267998d8245e2a395b98959db
Signed-off-by: Kyrollos Bekhet <kyrollos.bekhet@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/c/tracecompass/org.eclipse.tracecompass/+/194451
Tested-by: Trace Compass Bot <tracecompass-bot@eclipse.org>
Tested-by: Patrick Tasse <patrick.tasse@gmail.com>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Reviewed-by: Hoang Thuan Pham <hoangpham.eclipse@gmail.com>
Reviewed-by: Patrick Tasse <patrick.tasse@gmail.com>
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
diff --git a/analysis/org.eclipse.tracecompass.analysis.timing.core.tests/src/org/eclipse/tracecompass/analysis/timing/core/tests/segmentstore/SegmentStoreStatisticsDataProviderTest.java b/analysis/org.eclipse.tracecompass.analysis.timing.core.tests/src/org/eclipse/tracecompass/analysis/timing/core/tests/segmentstore/SegmentStoreStatisticsDataProviderTest.java
index 0bda644..b9cc7cd 100644
--- a/analysis/org.eclipse.tracecompass.analysis.timing.core.tests/src/org/eclipse/tracecompass/analysis/timing/core/tests/segmentstore/SegmentStoreStatisticsDataProviderTest.java
+++ b/analysis/org.eclipse.tracecompass.analysis.timing.core.tests/src/org/eclipse/tracecompass/analysis/timing/core/tests/segmentstore/SegmentStoreStatisticsDataProviderTest.java
@@ -50,7 +50,6 @@
  *
  * @author Bernd Hufmann
  */
-@SuppressWarnings("restriction")
 public class SegmentStoreStatisticsDataProviderTest {
 
     // ------------------------------------------------------------------------
diff --git a/analysis/org.eclipse.tracecompass.analysis.timing.core.tests/src/org/eclipse/tracecompass/analysis/timing/core/tests/segmentstore/SegmentStoreTableDataProviderTest.java b/analysis/org.eclipse.tracecompass.analysis.timing.core.tests/src/org/eclipse/tracecompass/analysis/timing/core/tests/segmentstore/SegmentStoreTableDataProviderTest.java
index 8d0f13f..f0f8159 100644
--- a/analysis/org.eclipse.tracecompass.analysis.timing.core.tests/src/org/eclipse/tracecompass/analysis/timing/core/tests/segmentstore/SegmentStoreTableDataProviderTest.java
+++ b/analysis/org.eclipse.tracecompass.analysis.timing.core.tests/src/org/eclipse/tracecompass/analysis/timing/core/tests/segmentstore/SegmentStoreTableDataProviderTest.java
@@ -17,6 +17,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -24,6 +25,7 @@
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.internal.analysis.timing.core.segmentstore.SegmentStoreTableDataProvider;
 import org.eclipse.tracecompass.internal.analysis.timing.core.segmentstore.SegmentStoreTableLine;
+import org.eclipse.tracecompass.internal.provisional.tmf.core.model.events.TmfEventTableDataProvider.Direction;
 import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters.VirtualTableQueryFilter;
 import org.eclipse.tracecompass.internal.provisional.tmf.core.model.table.ITmfVirtualTableDataProvider;
 import org.eclipse.tracecompass.internal.provisional.tmf.core.model.table.ITmfVirtualTableModel;
@@ -31,7 +33,7 @@
 import org.eclipse.tracecompass.internal.provisional.tmf.core.model.table.VirtualTableCell;
 import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
 import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
-import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
+import org.eclipse.tracecompass.tmf.core.model.CoreFilterProperty;
 import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
 import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
 import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
@@ -44,7 +46,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-/*
+/**
  * Tests the {@Link SegmentStoreTableDataProvider}
  *
  * @author: Kyrollos Bekhet
@@ -58,13 +60,20 @@
     private static final String DURATION_COLUMN_NAME = "Duration";
     private static Map<String, Long> fColumns = Collections.emptyMap();
     private static TmfXmlTraceStub fTrace;
+    private static final String TABLE_SEARCH_EXPRESSION_KEY = "table_search_expressions"; //$NON-NLS-1$
+    private static final String TABLE_SEARCH_DIRECTION_KEY = "table_search_direction"; //$NON-NLS-1$
 
+    /**
+     * Set-up resources
+     *
+     * @throws TmfAnalysisException
+     *             Trace exception should not happen
+     */
+    @SuppressWarnings("null")
     @BeforeClass
-    public static void init() throws TmfTraceException, TmfAnalysisException {
+    public static void init() throws TmfAnalysisException {
         fTrace = new TmfXmlTraceStubNs();
-        @NonNull
-        StubSegmentStoreProvider fixture = getValidSegment(fTrace);
-
+        @NonNull StubSegmentStoreProvider fixture = getValidSegment(fTrace);
         ITmfTrace trace = fTrace;
         assertNotNull(trace);
         fDataProvider = new SegmentStoreTableDataProvider(fTrace, fixture, "org.eclipse.tracecompass.analysis.timing.core.tests.segmentstore");
@@ -84,7 +93,7 @@
         if (columns == null) {
             return Collections.emptyMap();
         }
-        List<TmfTreeDataModel> columnEntries = columns.getEntries();
+        List<@NonNull TmfTreeDataModel> columnEntries = columns.getEntries();
         assertEquals(START_TIME_COLUMN_NAME, columnEntries.get(0).getName());
         assertEquals(END_TIME_COLUMN_NAME, columnEntries.get(1).getName());
         assertEquals(DURATION_COLUMN_NAME, columnEntries.get(2).getName());
@@ -96,6 +105,9 @@
         return expectedColumns;
     }
 
+    /**
+     * Disposes resources
+     */
     @AfterClass
     public static void tearDown() {
         fTrace.dispose();
@@ -105,6 +117,13 @@
     // Tests
     // ---------------------------------------------------------------------------
 
+    /**
+     * Test column returned by the provider.
+     *
+     * @throws InterruptedException
+     *             Exception thrown if test takes longer than 200ms.
+     */
+    @SuppressWarnings("null")
     @Test(timeout = 200)
     public void testDataProviderFetchColumn() throws InterruptedException {
         Long startTimeColumnId = fColumns.get(START_TIME_COLUMN_NAME);
@@ -115,7 +134,7 @@
         assertNotNull(endTimeColumnId);
         assertNotNull(durationColumnId);
 
-        List<TmfTreeDataModel> expectedColumnEntries = Arrays.asList(
+        List<@NonNull TmfTreeDataModel> expectedColumnEntries = Arrays.asList(
                 new TmfTreeDataModel(startTimeColumnId, -1, Collections.singletonList(START_TIME_COLUMN_NAME)),
                 new TmfTreeDataModel(endTimeColumnId, -1, Collections.singletonList(END_TIME_COLUMN_NAME)),
                 new TmfTreeDataModel(durationColumnId, -1, Collections.singletonList(DURATION_COLUMN_NAME)));
@@ -123,14 +142,22 @@
         TmfModelResponse<TmfTreeModel<@NonNull TmfTreeDataModel>> response = fDataProvider.fetchTree(FetchParametersUtils.timeQueryToMap(new TimeQueryFilter(0, 0, 1)), null);
         TmfTreeModel<@NonNull TmfTreeDataModel> currentColumnModel = response.getModel();
         assertNotNull(currentColumnModel);
-        List<TmfTreeDataModel> currentColumnEntries = currentColumnModel.getEntries();
+        List<@NonNull TmfTreeDataModel> currentColumnEntries = currentColumnModel.getEntries();
         assertEquals(expectedColumnEntries, currentColumnEntries);
     }
 
+    /**
+     * Test lines returned by the provider if the query starts from index zero.
+     *
+     * @throws InterruptedException
+     *             Exception thrown if test takes longer than 200ms
+     */
+    @SuppressWarnings("null")
     @Test(timeout = 200)
     public void testDataProviderFetchLineZeroIndex() throws InterruptedException {
         VirtualTableQueryFilter queryFilter = new VirtualTableQueryFilter(Collections.emptyList(), 0, 5);
-        List<SegmentStoreTableLine> expectedData = Arrays.asList(
+        @NonNull
+        List<@NonNull SegmentStoreTableLine> expectedData = Arrays.asList(
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(0)), new VirtualTableCell(lineTime(0)), new VirtualTableCell(lineDuration(0))), 0),
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(0)), new VirtualTableCell(lineTime(1)), new VirtualTableCell(lineDuration(1))), 1),
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(0)), new VirtualTableCell(lineTime(2)), new VirtualTableCell(lineDuration(2))), 2),
@@ -143,10 +170,18 @@
         assertEquals(expectedModel, currentModel);
     }
 
+    /**
+     * Test lines returned by the provider if the query starts from a non zero
+     * index.
+     *
+     * @throws InterruptedException
+     *             Exception thrown if test takes longer than 200ms.
+     */
+    @SuppressWarnings("null")
     @Test(timeout = 200)
     public void testDataProviderFetchLineNonZeroIndex() throws InterruptedException {
         VirtualTableQueryFilter queryFilter = new VirtualTableQueryFilter(Collections.emptyList(), 10, 5);
-        List<SegmentStoreTableLine> expectedData = Arrays.asList(
+        List<@NonNull SegmentStoreTableLine> expectedData = Arrays.asList(
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(7)), new VirtualTableCell(lineTime(10)), new VirtualTableCell(lineDuration(3))), 10),
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(7)), new VirtualTableCell(lineTime(11)), new VirtualTableCell(lineDuration(4))), 11),
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(7)), new VirtualTableCell(lineTime(12)), new VirtualTableCell(lineDuration(5))), 12),
@@ -159,10 +194,18 @@
         assertEquals(expectedModel, currentModel);
     }
 
+    /**
+     * Test the performance and efficiency of the fetch method of the provider
+     * by requesting a segment with a big rank in the segment store.
+     *
+     * @throws InterruptedException
+     *             Exception thrown if test takes longer than 200ms.
+     */
+    @SuppressWarnings("null")
     @Test(timeout = 200)
     public void testDataProviderFetchLineTimeOut() throws InterruptedException {
         VirtualTableQueryFilter queryFilter = new VirtualTableQueryFilter(Collections.emptyList(), 3200, 5);
-        List<SegmentStoreTableLine> expectedData = Arrays.asList(
+        List<@NonNull SegmentStoreTableLine> expectedData = Arrays.asList(
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(3199)), new VirtualTableCell(lineTime(3200)), new VirtualTableCell(lineDuration(1))), 3200),
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(3199)), new VirtualTableCell(lineTime(3201)), new VirtualTableCell(lineDuration(2))), 3201),
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(3199)), new VirtualTableCell(lineTime(3202)), new VirtualTableCell(lineDuration(3))), 3202),
@@ -175,12 +218,17 @@
         assertEquals(expectedModel, currentModel);
     }
 
-    // a test to make sure it can fetch from an index that corresponds to our
-    // step
+    /**
+     * Test lines returned by the provider.
+     *
+     * @throws InterruptedException
+     *             Exception thrown if test takes longer than 200ms.
+     */
+    @SuppressWarnings("null")
     @Test(timeout = 200)
     public void testDataProviderFetchLineCornerIndex() throws InterruptedException {
         VirtualTableQueryFilter queryFilter = new VirtualTableQueryFilter(Collections.emptyList(), 1000, 5);
-        List<SegmentStoreTableLine> expectedData = Arrays.asList(
+        List<@NonNull SegmentStoreTableLine> expectedData = Arrays.asList(
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(994)), new VirtualTableCell(lineTime(1000)), new VirtualTableCell(lineDuration(6))), 1000),
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(1001)), new VirtualTableCell(lineTime(1001)), new VirtualTableCell(lineDuration(0))), 1001),
                 new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(1001)), new VirtualTableCell(lineTime(1002)), new VirtualTableCell(lineDuration(1))), 1002),
@@ -193,19 +241,44 @@
         assertEquals(expectedModel, currentModel);
     }
 
-    /*
-     * This test test the case if we went beyond the endtime stored in the index
-     * To test this case, the count of the query must be big ex 2000 so in this
-     * way there will be some missing segments because segment number 1999 have
-     * a start time of 1998 that doesn't intersect with end time 999
+    /**
+     * Test lines returned by the provider starting from a given index and with
+     * a search filter applied.
      */
+    @SuppressWarnings("null")
+    @Test
+    public void testDataProviderFetchLineWithSearch() {
+        VirtualTableQueryFilter queryFilter = new VirtualTableQueryFilter(Collections.emptyList(), 0, 5);
+        Map<String, Object> fetchParameters = FetchParametersUtils.virtualTableQueryToMap(queryFilter);
+        Map<Long, String> searchMap = new HashMap<>();
+        searchMap.put(fColumns.get(START_TIME_COLUMN_NAME), lineTime(7000));
+        fetchParameters.put(TABLE_SEARCH_EXPRESSION_KEY, searchMap);
+        fetchParameters.put(TABLE_SEARCH_DIRECTION_KEY, Direction.NEXT);
+        List<@NonNull SegmentStoreTableLine> expectedData = Arrays.asList(
+                new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(7000)), new VirtualTableCell(lineTime(7000)), new VirtualTableCell(lineDuration(0))), 7000),
+                new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(7000)), new VirtualTableCell(lineTime(7001)), new VirtualTableCell(lineDuration(1))), 7001),
+                new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(7000)), new VirtualTableCell(lineTime(7002)), new VirtualTableCell(lineDuration(2))), 7002),
+                new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(7000)), new VirtualTableCell(lineTime(7003)), new VirtualTableCell(lineDuration(3))), 7003),
+                new SegmentStoreTableLine(Arrays.asList(new VirtualTableCell(lineTime(7000)), new VirtualTableCell(lineTime(7004)), new VirtualTableCell(lineDuration(4))), 7004));
+        expectedData.forEach(sl -> sl.setActiveProperties(CoreFilterProperty.HIGHLIGHT));
+        TmfModelResponse<ITmfVirtualTableModel<@NonNull SegmentStoreTableLine>> response = fDataProvider.fetchLines(fetchParameters, null);
+        ITmfVirtualTableModel<@NonNull SegmentStoreTableLine> currentModel = response.getModel();
+        assertNotNull(currentModel);
+        ITmfVirtualTableModel<@NonNull SegmentStoreTableLine> expectedModel = new TmfVirtualTableModel<>(new ArrayList<>(fColumns.values()), expectedData, 7000, 65535);
+        assertEquals(expectedModel, currentModel);
+    }
+
+    /**
+     * This test test the case if we went beyond the end time stored in the index
+     */
+    @SuppressWarnings("null")
     @Test
     public void testDataProviderFetchLineCrossIndexes() {
         final int count = 2000;
-        int previousStartTime = 0;
-        List<SegmentStoreTableLine> expectedData = new ArrayList<>();
+        long previousStartTime = 0;
+        List<@NonNull SegmentStoreTableLine> expectedData = new ArrayList<>();
         VirtualTableQueryFilter queryFilter = new VirtualTableQueryFilter(Collections.emptyList(), 0, count);
-        for (int i = 0; i < count; i++) {
+        for (long i = 0; i < count; i++) {
             if (i % 7 == 0) {
                 previousStartTime = i;
             }
diff --git a/analysis/org.eclipse.tracecompass.analysis.timing.core/src/org/eclipse/tracecompass/internal/analysis/timing/core/segmentstore/SegmentStoreTableDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.timing.core/src/org/eclipse/tracecompass/internal/analysis/timing/core/segmentstore/SegmentStoreTableDataProvider.java
index c01c038..a0e8cc7 100644
--- a/analysis/org.eclipse.tracecompass.analysis.timing.core/src/org/eclipse/tracecompass/internal/analysis/timing/core/segmentstore/SegmentStoreTableDataProvider.java
+++ b/analysis/org.eclipse.tracecompass.analysis.timing.core/src/org/eclipse/tracecompass/internal/analysis/timing/core/segmentstore/SegmentStoreTableDataProvider.java
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -28,6 +29,8 @@
 import java.util.function.Predicate;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
 import org.apache.commons.lang3.StringUtils;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jdt.annotation.Nullable;
@@ -46,16 +49,17 @@
 import org.eclipse.tracecompass.internal.tmf.core.model.filters.FetchParametersUtils;
 import org.eclipse.tracecompass.segmentstore.core.ISegment;
 import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
+import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
 import org.eclipse.tracecompass.tmf.core.TmfStrings;
 import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
 import org.eclipse.tracecompass.tmf.core.dataprovider.DataProviderParameterUtils;
 import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
 import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeDataModel;
+import org.eclipse.tracecompass.tmf.core.model.CoreFilterProperty;
 import org.eclipse.tracecompass.tmf.core.model.tree.TmfTreeModel;
 import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
 import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
 import org.eclipse.tracecompass.tmf.core.segment.ISegmentAspect;
-import org.eclipse.tracecompass.tmf.core.segment.SegmentStartTimeAspect;
 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 
@@ -105,9 +109,9 @@
         private final long fStartTime;
         private long fCount;
 
-        public SegmentPredicate(long startTime, long count) {
-            fStartTime = startTime;
-            fCount = count;
+        public SegmentPredicate(SegmentStoreIndex segmentIndex) {
+            fStartTime = segmentIndex.getStartTimestamp();
+            fCount = segmentIndex.getCounter();
         }
 
         @Override
@@ -123,14 +127,51 @@
             }
             return false;
         }
+
     }
 
-    public static final String ID = "org.eclipse.tracecompass.analysis.timing.core.segmentstore.SegmentStoreTableDataProvider";
+    /**
+     * A placeholder class to wrap a segment and its rank.
+     *
+     * @author Kyrollos Bekhet
+     *
+     */
+    private static class WrappedSegment {
+        private ISegment fSegment;
+        private long fRank;
+
+        public WrappedSegment(ISegment segment, long rank) {
+            fSegment = segment;
+            fRank = rank;
+        }
+
+        public ISegment getOriginalSegment() {
+            return fSegment;
+        }
+
+        public long getRank() {
+            return fRank;
+        }
+    }
+
+    private enum Direction {
+        /** Search next */
+        NEXT,
+        /** Search previous */
+        PREVIOUS
+    }
+
+    /**
+     * The id of the data provider
+     */
+    public static final String ID = "org.eclipse.tracecompass.analysis.timing.core.segmentstore.SegmentStoreTableDataProvider"; //$NON-NLS-1$
     private static final AtomicLong fAtomicLong = new AtomicLong();
     private static BiMap<ISegmentAspect, Long> fAspectToIdMap = HashBiMap.create();
-    private static final Format FORMATTER = new DecimalFormat("###,###.##");
-    private static final long STEP = 1000;
+    private static final Format FORMATTER = new DecimalFormat("###,###.##"); //$NON-NLS-1$
+    private static final int STEP = 1000;
     private static final Logger LOGGER = TraceCompassLog.getLogger(SegmentStoreTableDataProvider.class);
+    private static final String TABLE_SEARCH_EXPRESSION_KEY = "table_search_expressions"; //$NON-NLS-1$
+    private static final String TABLE_SEARCH_DIRECTION_KEY = "table_search_direction"; //$NON-NLS-1$
 
     private final Object fLock = new Object();
     private final String fId;
@@ -144,29 +185,28 @@
      * @param trace
      *            A trace on which we are interested to fetch a segment store
      *            table model.
-     *
      * @param segmentProvider
      *            The segment provider that contains the data and from which the
      *            data will be fetched.
-     *
      * @param analysisId
      *            The analysis identifier.
      */
+    @SuppressWarnings("null")
     public SegmentStoreTableDataProvider(ITmfTrace trace, ISegmentStoreProvider segmentProvider, String analysisId) {
         super(trace);
         TraceCompassLogUtils.traceObjectCreation(LOGGER, Level.FINE, this);
         fId = analysisId;
-        fComparator = (Comparator<ISegment>) SegmentStartTimeAspect.SEGMENT_START_TIME_ASPECT.getComparator();
-        if (segmentProvider.getSegmentStore() != null) {
-            buildIndex(segmentProvider);
-        } else {
-            if (segmentProvider instanceof IAnalysisModule) {
-                ((IAnalysisModule) segmentProvider).schedule();
-                ((IAnalysisModule) segmentProvider).waitForCompletion();
-                buildIndex(segmentProvider);
-            }
+        fComparator = SegmentComparators.INTERVAL_START_COMPARATOR.thenComparing(Comparator.comparingLong(ISegment::getLength));
+        ISegmentStore<ISegment> segments = segmentProvider.getSegmentStore();
+        if (segments == null && segmentProvider instanceof IAnalysisModule) {
+            ((IAnalysisModule) segmentProvider).schedule();
+            ((IAnalysisModule) segmentProvider).waitForCompletion();
         }
-
+        fSegmentProvider = segmentProvider;
+        segments = segmentProvider.getSegmentStore();
+        if (segments != null) {
+            buildIndex(segments);
+        }
     }
 
     @Override
@@ -180,41 +220,41 @@
      * @param segmentProvider
      *            The segment provider to use.
      */
-    private void buildIndex(ISegmentStoreProvider segmentProvider) {
-        TraceCompassLogUtils.traceAsyncStart(LOGGER, Level.FINE, "SegmentStoreTableDataProvider#buildIndex", fId, 0);
-        fSegmentProvider = segmentProvider;
-        long i = 0;
-        if (fSegmentProvider != null) {
-            ISegmentStore<ISegment> segStore = fSegmentProvider.getSegmentStore();
-            if (segStore != null) {
-                synchronized (fLock) {
-                    try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "SegmentStoreTableDataProvider#buildIndex.buildingIndexes").build()) {
-                        TraceCompassLogUtils.traceObjectCreation(LOGGER, Level.FINE, fLock);
-                        Iterable<ISegment> sortedSegmentStore = segStore.iterator(fComparator);
-                        long counter = 0;
-                        long previousTimestamp = Long.MAX_VALUE;
-                        for (ISegment segment : sortedSegmentStore) {
-                            if (segment.getStart() == previousTimestamp) {
-                                counter++;
-                            } else {
-                                previousTimestamp = segment.getStart();
-                                counter = 0;
-                            }
-                            if (i % STEP == 0) {
-                                fIndexes.add(new SegmentStoreIndex(segment.getStart(), counter));
-                            }
-                            i++;
-                        }
-                    } catch (Exception ex) {
-                        TraceCompassLogUtils.traceInstant(LOGGER, Level.SEVERE, "error build index", ex.getMessage());
-                    } finally {
-                        TraceCompassLogUtils.traceObjectDestruction(LOGGER, Level.FINE, fLock);
-                    }
-                }
+    private void buildIndex(ISegmentStore<ISegment> segments) {
+        TraceCompassLogUtils.traceAsyncStart(LOGGER, Level.FINE, "SegmentStoreTableDataProvider#buildIndex", fId, 0); //$NON-NLS-1$
+        synchronized (fLock) {
+            try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "SegmentStoreTableDataProvider#buildIndex.buildingIndexes").build()) { //$NON-NLS-1$
+                TraceCompassLogUtils.traceObjectCreation(LOGGER, Level.FINE, fLock);
+                Iterable<ISegment> sortedSegmentStore = segments.iterator(fComparator);
+                fIndexes = getIndexes(sortedSegmentStore);
+            } catch (Exception ex) {
+                TraceCompassLogUtils.traceInstant(LOGGER, Level.SEVERE, "error build index", ex.getMessage()); //$NON-NLS-1$
+            } finally {
+                TraceCompassLogUtils.traceObjectDestruction(LOGGER, Level.FINE, fLock);
             }
         }
     }
 
+    private static List<SegmentStoreIndex> getIndexes(Iterable<ISegment> segmentStore) {
+        long counter = 0;
+        long i = 0;
+        long previousTimestamp = Long.MAX_VALUE;
+        List<SegmentStoreIndex> indexes = new ArrayList<>();
+        for (ISegment segment : segmentStore) {
+            if (segment.getStart() == previousTimestamp) {
+                counter++;
+            } else {
+                previousTimestamp = segment.getStart();
+                counter = 0;
+            }
+            if (i % STEP == 0) {
+                indexes.add(new SegmentStoreIndex(segment.getStart(), counter));
+            }
+            i++;
+        }
+        return indexes;
+    }
+
     @Override
     public String getId() {
         return fId;
@@ -242,10 +282,8 @@
 
     @Override
     public TmfModelResponse<ITmfVirtualTableModel<SegmentStoreTableLine>> fetchLines(Map<String, Object> fetchParameters, @Nullable IProgressMonitor monitor) {
-        TraceCompassLogUtils.traceAsyncStart(LOGGER, Level.FINE, "SegmentStoreTableDataProvider#fetchLines", fId, 2);
-        if (!fetchParameters.containsKey(DataProviderParameterUtils.REQUESTED_COLUMN_IDS_KEY)) {
-            fetchParameters.put(DataProviderParameterUtils.REQUESTED_COLUMN_IDS_KEY, Collections.emptyList());
-        }
+        TraceCompassLogUtils.traceAsyncStart(LOGGER, Level.FINE, "SegmentStoreTableDataProvider#fetchLines", fId, 2); //$NON-NLS-1$
+        fetchParameters.putIfAbsent(DataProviderParameterUtils.REQUESTED_COLUMN_IDS_KEY, Collections.emptyList());
         VirtualTableQueryFilter queryFilter = FetchParametersUtils.createVirtualTableQueryFilter(fetchParameters);
         if (queryFilter == null) {
             return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.INCORRECT_QUERY_PARAMETERS);
@@ -265,33 +303,11 @@
                     return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
                 }
                 synchronized (fLock) {
-                    try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "SegmentStoreTableDataProvider#fetchLines").build()) {
+                    try (FlowScopeLog scope = new FlowScopeLogBuilder(LOGGER, Level.FINE, "SegmentStoreTableDataProvider#fetchLines").build()) { //$NON-NLS-1$
                         TraceCompassLogUtils.traceObjectCreation(LOGGER, Level.FINE, fLock);
-                        List<SegmentStoreTableLine> lines = new ArrayList<>();
-                        final int startIndexRank = (int) (queryFilter.getIndex() / STEP);
-                        final int actualStartQueryIndex = (int) (queryFilter.getIndex() % STEP);
-                        SegmentStoreIndex segIndex = fIndexes.get(startIndexRank);
-                        long start = segIndex.getStartTimestamp();
-                        int endIndexRank = (int) ((queryFilter.getIndex() + queryFilter.getCount() + STEP - 1) / STEP);
-                        long end;
-                        if (endIndexRank < fIndexes.size()) {
-                            end = fIndexes.get(endIndexRank).getStartTimestamp();
-                        } else {
-                            end = Long.MAX_VALUE;
-                        }
-                        SegmentPredicate filter = new SegmentPredicate(start, segIndex.getCounter());
-                        List<ISegment> newSegStore = segStore.getIntersectingElements(start, end, fComparator, filter);
-                        for (int i = actualStartQueryIndex; i < newSegStore.size(); i++) {
-                            if (queryFilter.getCount() == lines.size()) {
-                                break;
-                            }
-                            long lineNumber = queryFilter.getIndex() + lines.size();
-                            SegmentStoreTableLine newLine = buildSegmentStoreTableLine(aspects, newSegStore.get(i), lineNumber);
-                            lines.add(newLine);
-                        }
-                        return new TmfModelResponse<>(new TmfVirtualTableModel<>(columnIds, lines, queryFilter.getIndex(), segStore.size()), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+                        return extractRequestedLines(queryFilter, fetchParameters, segStore, aspects);
                     } catch (Exception ex) {
-                        TraceCompassLogUtils.traceInstant(LOGGER, Level.SEVERE, "error fetching lines ", ex.getMessage());
+                        TraceCompassLogUtils.traceInstant(LOGGER, Level.SEVERE, "error fetching lines ", ex.getMessage()); //$NON-NLS-1$
                     } finally {
                         TraceCompassLogUtils.traceObjectDestruction(LOGGER, Level.FINE, fLock);
                     }
@@ -301,43 +317,247 @@
         return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, CommonStatusMessage.INCORRECT_QUERY_PARAMETERS);
     }
 
+    private TmfModelResponse<ITmfVirtualTableModel<SegmentStoreTableLine>> extractRequestedLines(VirtualTableQueryFilter queryFilter, Map<String, Object> fetchParameters, ISegmentStore<ISegment> segmentStore, Map<Long, ISegmentAspect> aspects) {
+        VirtualTableQueryFilter localQueryFilter = queryFilter;
+        @Nullable Predicate<ISegment> searchFilter = generateFilter(fetchParameters);
+        List<Long> columnIds = new ArrayList<>(aspects.keySet());
+        List<SegmentStoreTableLine> lines = new ArrayList<>();
+        int startIndexRank = (int) (localQueryFilter.getIndex() / STEP);
+        int actualStartQueryIndex = (int) (queryFilter.getIndex() % STEP);
+        SegmentStoreIndex segIndex = fIndexes.get(startIndexRank);
+        long start = segIndex.getStartTimestamp();
+        SegmentPredicate filter = new SegmentPredicate(segIndex);
+        int endIndexRank = (int) ((localQueryFilter.getIndex() + localQueryFilter.getCount() + STEP - 1) / STEP);
+        long end = getEndTimestamp(endIndexRank);
+        /*
+         * Search for the next or previous segment starting from the given
+         * segment index
+         */
+        Object directionValue = fetchParameters.get(TABLE_SEARCH_DIRECTION_KEY);
+        if (searchFilter != null && directionValue != null) {
+            Direction direction = directionValue.equals(Direction.PREVIOUS.name()) ? Direction.PREVIOUS : Direction.NEXT;
+            @Nullable WrappedSegment segment = null;
+            if (direction == Direction.NEXT) {
+                segment = getNextWrappedSegmentMatching(searchFilter, queryFilter.getIndex(), segmentStore);
+            } else {
+                segment = getPreviousWrappedSegmentMatching(searchFilter, queryFilter.getIndex(), segmentStore);
+            }
+            if (segment != null) {
+                lines.add(buildSegmentStoreTableLine(aspects, segment.getOriginalSegment(), segment.getRank(), searchFilter));
+                localQueryFilter = new VirtualTableQueryFilter(queryFilter.getColumnsId(), segment.getRank(), queryFilter.getCount());
+                long nextSegmentRank = segment.getRank() + 1;
+                startIndexRank = (int) (nextSegmentRank / STEP);
+                actualStartQueryIndex = (int) (nextSegmentRank % STEP);
+                segIndex = fIndexes.get(startIndexRank);
+                start = segIndex.getStartTimestamp();
+                endIndexRank = (int) ((nextSegmentRank + localQueryFilter.getCount() + STEP - 1) / STEP);
+                end = getEndTimestamp(endIndexRank);
+            }
+            if ((queryFilter.getCount() == 1) || (segment == null)) {
+                return new TmfModelResponse<>(new TmfVirtualTableModel<>(columnIds, lines, localQueryFilter.getIndex(), segmentStore.size()), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+            }
+        }
+        List<ISegment> newSegStore = segmentStore.getIntersectingElements(start, end, fComparator, filter);
+        for (int i = actualStartQueryIndex; i < newSegStore.size(); i++) {
+            if (queryFilter.getCount() == lines.size()) {
+                break;
+            }
+            long lineNumber = localQueryFilter.getIndex() + lines.size();
+            SegmentStoreTableLine newLine = buildSegmentStoreTableLine(aspects, newSegStore.get(i), lineNumber, searchFilter);
+            lines.add(newLine);
+        }
+        return new TmfModelResponse<>(new TmfVirtualTableModel<>(columnIds, lines, localQueryFilter.getIndex(), segmentStore.size()), ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
+    }
+
+    /**
+     * Retrieve from a segment store the next segment starting from a given
+     * index, matching the given predicate.
+     *
+     * @param searchFilter
+     *            The predicate to match
+     * @param startQueryIndex
+     *            The index of the query
+     * @param segmentStore
+     *            The segment store from where the segments will be fetched.
+     *
+     * @return A {@link WrappedSegment} that contains the matching next segment
+     *         found after a given index.
+     */
+    private @Nullable WrappedSegment getNextWrappedSegmentMatching(Predicate<ISegment> searchFilter, long startQueryIndex, ISegmentStore<ISegment> segmentStore) {
+        int startTimeIndexRank = (int) (startQueryIndex / STEP);
+        int actualStartQueryIndex = (int) (startQueryIndex % STEP);
+        int endTimeIndexRank = startTimeIndexRank + 1;
+        while (startTimeIndexRank < fIndexes.size()) {
+            SegmentStoreIndex segIndex = fIndexes.get(startTimeIndexRank);
+            SegmentPredicate filter = new SegmentPredicate(segIndex);
+            long end = getEndTimestamp(endTimeIndexRank);
+            List<ISegment> segments = segmentStore.getIntersectingElements(segIndex.getStartTimestamp(), end, fComparator, filter);
+            for (int i = actualStartQueryIndex; i < segments.size(); i++) {
+                ISegment segment = segments.get(i);
+                if (searchFilter.test(segment)) {
+                    long rank = ((long) startTimeIndexRank * STEP) + i;
+                    return new WrappedSegment(segment, rank);
+                }
+            }
+            actualStartQueryIndex = 0;
+            startTimeIndexRank++;
+            endTimeIndexRank++;
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve from a segment store the previous segment, from a given rank,
+     * matching the given predicate.
+     *
+     * @param searchFilter
+     *            The predicate to match
+     * @param endQueryIndex
+     *            The index of the query
+     * @param segmentStore
+     *            The segment store from where the segments will be fetched
+     *
+     * @return A {@link WrappedSegment} that contains the matching previous
+     *         segment found before a given index.
+     */
+    private @Nullable WrappedSegment getPreviousWrappedSegmentMatching(Predicate<ISegment> searchFilter, long endQueryIndex, ISegmentStore<ISegment> segmentStore) {
+        int actualEndQueryIndex = (int) (endQueryIndex % STEP);
+        int startTimeIndexRank = (int) (endQueryIndex / STEP);
+        int endTimeIndexRank = startTimeIndexRank + 1;
+        while (endTimeIndexRank > 0) {
+            SegmentStoreIndex segIndex = fIndexes.get(startTimeIndexRank);
+            SegmentPredicate filter = new SegmentPredicate(segIndex);
+            long end = getEndTimestamp(endTimeIndexRank);
+            List<ISegment> segments = segmentStore.getIntersectingElements(segIndex.getStartTimestamp(), end, fComparator, filter);
+            for (int i = Math.min(segments.size() - 1, actualEndQueryIndex); i >= 0; i--) {
+                if (searchFilter.test(segments.get(i))) {
+                    long rank = i + ((long) startTimeIndexRank * STEP);
+                    return new WrappedSegment(segments.get(i), rank);
+                }
+            }
+            actualEndQueryIndex = STEP;
+            startTimeIndexRank--;
+            endTimeIndexRank--;
+        }
+        return null;
+    }
+
+    /**
+     * Generates a predicate filter based on the search map found in the given
+     * query parameters
+     *
+     * @param fetchParameters
+     *            The query parameters used to extract the search map
+     *
+     * @return A predicate based on the search map found in the fetch parameters
+     */
+    private static @Nullable Predicate<ISegment> generateFilter(Map<String, Object> fetchParameters) {
+        @Nullable Map<Long, String> searchMap = extractSearchFilter(fetchParameters);
+        if (searchMap == null) {
+            return null;
+        }
+        Map<Long, Pattern> patterns = new HashMap<>();
+        for (Entry<Long, String> searchEntry : searchMap.entrySet()) {
+            patterns.put(searchEntry.getKey(), Pattern.compile(searchEntry.getValue()));
+        }
+        Map<Long, ISegmentAspect> aspects = fAspectToIdMap.inverse();
+        return segment -> {
+            for (Entry<Long, Pattern> patternEntry : patterns.entrySet()) {
+                Pattern pattern = Objects.requireNonNull(patternEntry.getValue());
+                ISegmentAspect aspect = aspects.get(patternEntry.getKey());
+                if (aspect != null && !pattern.matcher(formatResolvedAspect(aspect.resolve(segment), aspect.getName())).find()) {
+                    return false;
+                }
+            }
+            return true;
+        };
+    }
+
+    @SuppressWarnings("unchecked")
+    private static @Nullable Map<Long, String> extractSearchFilter(Map<String, Object> fetchParameters) {
+        Object searchFilterObject = fetchParameters.get(TABLE_SEARCH_EXPRESSION_KEY);
+        if (searchFilterObject instanceof Map<?, ?>) {
+            return extractSimpleSearchFilter((Map<?, String>) searchFilterObject);
+        }
+        return null;
+    }
+
+    private static @Nullable Map<Long, String> extractSimpleSearchFilter(Map<?, String> searchFilterObject) {
+        if (searchFilterObject.isEmpty()) {
+            return null;
+        }
+        Map<Long, String> searchMap = new HashMap<>();
+        for (Entry<?, String> searchEntry : searchFilterObject.entrySet()) {
+            Long key = extractColumnId(searchEntry.getKey());
+            if (key != null) {
+                searchMap.put(key, searchEntry.getValue());
+            }
+        }
+        return searchMap;
+    }
+
+    /**
+     * Extract the id of the column out of an object
+     *
+     * @param key
+     *            The object that contains the id
+     *
+     * @return The column id
+     */
+    private static @Nullable Long extractColumnId(@Nullable Object key) {
+        try {
+            if (key instanceof String && Pattern.compile("[-?\\d+\\.?\\d+]").matcher((String) key).matches()) { //$NON-NLS-1$
+                return Long.valueOf((String) key);
+            }
+            if (key instanceof Long) {
+                return (Long) key;
+            }
+            if (key instanceof Integer) {
+                return Long.valueOf((Integer) key);
+            }
+        } catch (NumberFormatException e) {
+            // Do nothing
+        }
+        return null;
+    }
+
     /**
      * Builds the table line.
      *
      * @param aspects
      *            The aspects to resolve.
-     *
      * @param segment
      *            The segment that contains the data that will fill the line.
-     *
      * @param lineNumber
      *            The line number that will be assigned to the line that will be
      *            built.
+     * @param searchFilter
+     *            The predicate that applies the search to set the active
+     *            property
      *
-     * @return Returns a SegmentStoreTableLine that contains the data which will
-     *         be displayed by the client.
+     * @return Returns a SegmentStoreTableLine after resolving the aspects of a
+     *         given segment
      */
-    private static SegmentStoreTableLine buildSegmentStoreTableLine(Map<Long, ISegmentAspect> aspects, ISegment segment, long lineNumber) {
+    private static SegmentStoreTableLine buildSegmentStoreTableLine(Map<Long, ISegmentAspect> aspects, ISegment segment, long lineNumber, @Nullable Predicate<ISegment> searchFilter) {
         List<VirtualTableCell> entry = new ArrayList<>(aspects.size());
         for (Entry<Long, ISegmentAspect> aspectEntry : aspects.entrySet()) {
-            Object aspectResolved = aspectEntry.getValue().resolve(segment);
-            String cellContent;
-            if (aspectEntry.getValue().getName().equals(TmfStrings.duration())) {
-                cellContent = NonNullUtils.nullToEmptyString(FORMATTER.format(aspectResolved));
-            } else if (aspectEntry.getValue().getName().equals(TmfStrings.startTime()) || aspectEntry.getValue().getName().equals(TmfStrings.endTime())) {
-                cellContent = TmfTimestamp.fromNanos((Long) Objects.requireNonNull(aspectResolved)).toString();
-            } else {
-                cellContent = aspectResolved == null ? StringUtils.EMPTY : String.valueOf(aspectResolved);
-            }
+            ISegmentAspect aspect = Objects.requireNonNull(aspectEntry.getValue());
+            Object aspectResolved = aspect.resolve(segment);
+            String cellContent = formatResolvedAspect(aspectResolved, aspect.getName());
             entry.add(new VirtualTableCell(cellContent));
         }
         SegmentStoreTableLine tableLine = new SegmentStoreTableLine(entry, lineNumber);
+        if (searchFilter != null) {
+            tableLine.setActiveProperties(searchFilter.test(segment) ? CoreFilterProperty.HIGHLIGHT : 0);
+        }
         return tableLine;
     }
 
     /**
+     * Returns the desired {@link ISegmentAspect}.
+     *
      * @param desiredColumns
-     *            The list of desired column ids that we want to retreive
+     *            The list of desired column ids that we want to retrieve
      *
      * @return The list of {@link ISegmentAspect} that matches the desired
      *         columns ids
@@ -356,4 +576,35 @@
         return Objects.requireNonNull(fAspectToIdMap.inverse());
     }
 
+    /**
+     * Formats the resolved aspect into a string.
+     *
+     * @param aspectResolved
+     *            The object that results of the resolution of the segment.
+     * @param aspectName
+     *            The name of the aspect used to identify the strategy of
+     *            formatting.
+     *
+     * @return a literal string of the content of the aspect resolved object.
+     */
+    private static String formatResolvedAspect(@Nullable Object aspectResolved, String aspectName) {
+        String aspectParsed;
+        if (aspectName.equals(TmfStrings.duration())) {
+            aspectParsed = NonNullUtils.nullToEmptyString(FORMATTER.format(aspectResolved));
+        } else if (aspectName.equals(TmfStrings.startTime()) || aspectName.equals(TmfStrings.endTime())) {
+            aspectParsed = String.valueOf(TmfTimestamp.fromNanos((Long) Objects.requireNonNull(aspectResolved)));
+
+        } else {
+            aspectParsed = aspectResolved == null ? StringUtils.EMPTY : String.valueOf(aspectResolved);
+        }
+        return aspectParsed;
+    }
+
+    private long getEndTimestamp(int index) {
+        if (index >= fIndexes.size()) {
+            return Long.MAX_VALUE;
+        }
+        return fIndexes.get(index).getStartTimestamp();
+    }
+
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF b/tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF
index 06344f0..500c2e7 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF
+++ b/tmf/org.eclipse.tracecompass.tmf.core/META-INF/MANIFEST.MF
@@ -20,10 +20,14 @@
  org.apache.commons.lang3
 Export-Package: org.eclipse.tracecompass.internal.provisional.tmf.core.model,
  org.eclipse.tracecompass.internal.provisional.tmf.core.model.annotations;x-internal:=true,
- org.eclipse.tracecompass.internal.provisional.tmf.core.model.events;x-friends:="org.eclipse.tracecompass.tmf.core.tests",
+ org.eclipse.tracecompass.internal.provisional.tmf.core.model.events;x-friends:="org.eclipse.tracecompass.tmf.core.tests,org.eclipse.tracecompass.analysis.timing.core.tests",
  org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser;x-friends:="org.eclipse.tracecompass.tmf.ui,org.eclipse.tracecompass.tmf.core.tests",
- org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters;x-friends:="org.eclipse.tracecompass.tmf.core.tests,org.eclipse.tracecompass.tmf.ui",
- org.eclipse.tracecompass.internal.provisional.tmf.core.model.table;x-friends:="org.eclipse.tracecompass.tmf.core.tests",
+ org.eclipse.tracecompass.internal.provisional.tmf.core.model.filters;
+  x-friends:="org.eclipse.tracecompass.tmf.core.tests,
+   org.eclipse.tracecompass.tmf.ui,
+   org.eclipse.tracecompass.analysis.timing.core,
+   org.eclipse.tracecompass.analysis.timing.core.tests",
+ org.eclipse.tracecompass.internal.provisional.tmf.core.model.table;x-friends:="org.eclipse.tracecompass.tmf.core.tests,org.eclipse.tracecompass.analysis.timing.core,org.eclipse.tracecompass.analysis.timing.core.tests",
  org.eclipse.tracecompass.internal.provisional.tmf.core.model.timegraph;x-friends:="org.eclipse.tracecompass.analysis.os.linux.core,org.eclipse.tracecompass.analysis.os.linux.ui",
  org.eclipse.tracecompass.internal.tmf.core;x-friends:="org.eclipse.tracecompass.tmf.core.tests,org.eclipse.tracecompass.tmf.ui.swtbot.tests",
  org.eclipse.tracecompass.internal.tmf.core.analysis;x-friends:="org.eclipse.tracecompass.tmf.core.tests",
@@ -57,13 +61,14 @@
    org.eclipse.tracecompass.analysis.timing.ui,
    org.eclipse.tracecompass.incubator.callstack.core,
    org.eclipse.tracecompass.incubator.callstack.ui,
-   org.eclipse.tracecompass.tmf.analysis.xml.core,
-   org.eclipse.tracecompass.tmf.ui,
-   org.eclipse.tracecompass.tmf.core.tests,
    org.eclipse.tracecompass.lttng2.ust.core,
+   org.eclipse.tracecompass.tmf.analysis.xml.core,
    org.eclipse.tracecompass.tmf.analysis.xml.core.tests,
    org.eclipse.tracecompass.tmf.analysis.xml.ui,
-   org.eclipse.tracecompass.tmf.ctf.core.tests",
+   org.eclipse.tracecompass.tmf.core.tests,
+   org.eclipse.tracecompass.tmf.ctf.core.tests,
+   org.eclipse.tracecompass.tmf.ui,
+   org.eclipse.tracecompass.analysis.timing.core.tests",
  org.eclipse.tracecompass.internal.tmf.core.model.timegraph;
   x-friends:="org.eclipse.tracecompass.analysis.os.linux.core,
    org.eclipse.tracecompass.tmf.analysis.xml.core,