model: Move IFilterableDataModel in IElementResolver

Deprecates IFilterableDataModel and move its content to
IElementResolver.

These 2 classes serve similar purposes: allow various elements to
provide data on which to search and filter. One is a multimap to allow
an element to have multiple values for a single key, while the other did
not allow that. Only multimap remains now. The map method is deprecated.

The filters have been updated to supported multimap as inputs.

Change-Id: I2e258adc1df22a3f4274907630e7788abc995635
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/140707
Reviewed-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Tested-by: CI Bot
diff --git a/analysis/org.eclipse.tracecompass.analysis.graph.core/src/org/eclipse/tracecompass/internal/analysis/graph/core/dataprovider/CriticalPathDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.graph.core/src/org/eclipse/tracecompass/internal/analysis/graph/core/dataprovider/CriticalPathDataProvider.java
index 6197912..3b4ebf6 100644
--- a/analysis/org.eclipse.tracecompass.analysis.graph.core/src/org/eclipse/tracecompass/internal/analysis/graph/core/dataprovider/CriticalPathDataProvider.java
+++ b/analysis/org.eclipse.tracecompass.analysis.graph.core/src/org/eclipse/tracecompass/internal/analysis/graph/core/dataprovider/CriticalPathDataProvider.java
@@ -53,6 +53,7 @@
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.TreeMultimap;
 
 /**
@@ -164,7 +165,7 @@
             return new TmfModelResponse<>(null, Status.COMPLETED, CommonStatusMessage.COMPLETED);
         }
 
-        Map<@NonNull Integer, @NonNull Predicate<@NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         if (filter instanceof TimeGraphStateQueryFilter) {
             TimeGraphStateQueryFilter timeEventFilter = (TimeGraphStateQueryFilter) filter;
             predicates.putAll(computeRegexPredicate(timeEventFilter));
@@ -184,7 +185,7 @@
                     if (overlaps(state.getStartTime(), state.getDuration(), filter.getTimesRequested())) {
                         // Reset the properties for this state before filtering
                         state.setActiveProperties(0);
-                        addToStateList(filteredStates, state, id, predicates, monitor);
+                        applyFilterAndAddState(filteredStates, state, id, predicates, monitor);
                     }
                 }
                 rowModels.add(new TimeGraphRowModel(id, filteredStates));
diff --git a/analysis/org.eclipse.tracecompass.analysis.graph.core/src/org/eclipse/tracecompass/internal/analysis/graph/core/dataprovider/CriticalPathEntry.java b/analysis/org.eclipse.tracecompass.analysis.graph.core/src/org/eclipse/tracecompass/internal/analysis/graph/core/dataprovider/CriticalPathEntry.java
index c96e6ef..221833c 100644
--- a/analysis/org.eclipse.tracecompass.analysis.graph.core/src/org/eclipse/tracecompass/internal/analysis/graph/core/dataprovider/CriticalPathEntry.java
+++ b/analysis/org.eclipse.tracecompass.analysis.graph.core/src/org/eclipse/tracecompass/internal/analysis/graph/core/dataprovider/CriticalPathEntry.java
@@ -14,7 +14,7 @@
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.analysis.graph.core.base.IGraphWorker;
 import org.eclipse.tracecompass.internal.analysis.graph.core.base.TmfGraphStatistics;
-import org.eclipse.tracecompass.tmf.core.model.IFilterableDataModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.IElementResolver;
 import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
 
 import com.google.common.collect.HashMultimap;
@@ -25,7 +25,7 @@
  *
  * @author Loic Prieur-Drevon
  */
-public class CriticalPathEntry extends TimeGraphEntryModel implements IFilterableDataModel {
+public class CriticalPathEntry extends TimeGraphEntryModel implements IElementResolver {
     private final Long fSum;
     private final Double fPercent;
     private final @NonNull Multimap<@NonNull String, @NonNull String> fAspects = HashMultimap.create();
diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/resourcesstatus/ResourcesStatusDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/resourcesstatus/ResourcesStatusDataProvider.java
index 281f303..e8b05f2 100644
--- a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/resourcesstatus/ResourcesStatusDataProvider.java
+++ b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/resourcesstatus/ResourcesStatusDataProvider.java
@@ -20,6 +20,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.NavigableSet;
 import java.util.Objects;
 import java.util.Set;
@@ -53,8 +54,10 @@
 
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.TreeMultimap;
 import com.google.common.primitives.Ints;
 
@@ -294,7 +297,7 @@
             intervals.put(interval.getAttribute(), interval);
         }
 
-        Map<@NonNull Integer, @NonNull Predicate<@NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         if (filter instanceof TimeGraphStateQueryFilter) {
             TimeGraphStateQueryFilter timeEventFilter = (TimeGraphStateQueryFilter) filter;
             predicates.putAll(computeRegexPredicate(timeEventFilter));
@@ -320,11 +323,11 @@
                     if (type == Type.CPU && s == StateValues.CPU_STATUS_RUN_SYSCALL) {
                         // add events for all the sampled current threads.
                         List<@NonNull ITimeGraphState> syscalls = getSyscalls(ss, interval, intervals.get(currentThreadQuark));
-                        syscalls.forEach(timeGraphState -> addToStateList(eventList, timeGraphState, key, predicates, monitor));
+                        syscalls.forEach(timeGraphState -> applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor));
                     } else if (type == Type.CPU && s == StateValues.CPU_STATUS_RUN_USERMODE) {
                         // add events for all the sampled current threads.
                         List<@NonNull TimeGraphState> currentThreads = getCurrentThreads(ss, interval, intervals.get(currentThreadQuark));
-                        currentThreads.forEach(timeGraphState -> addToStateList(eventList, timeGraphState, key, predicates, monitor));
+                        currentThreads.forEach(timeGraphState -> applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor));
                     } else if (type == Type.CURRENT_THREAD && s != 0) {
                         String execName = null;
                         synchronized (fExecNamesCache) {
@@ -344,23 +347,23 @@
                             }
                         }
                         TimeGraphState timeGraphState = new TimeGraphState(startTime, duration, s, execName != null ? execName + ' ' + '(' + String.valueOf(s) + ')' : String.valueOf(s));
-                        addToStateList(eventList, timeGraphState, key, predicates, monitor);
+                        applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
                     } else if (type == Type.CURRENT_THREAD) {
                         // add null state when current thread is 0
                         ITimeGraphState timeGraphState = new TimeGraphState(startTime, duration, Integer.MIN_VALUE);
-                        addToStateList(eventList, timeGraphState, key, predicates, monitor);
+                        applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
                     } else {
                         TimeGraphState timeGraphState = new TimeGraphState(startTime, duration, s);
-                        addToStateList(eventList, timeGraphState, key, predicates, monitor);
+                        applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
                     }
                 } else if ((status instanceof Long) && (type == Type.FREQUENCY)) {
                     long s = (long) status;
                     // The value needs to fit in an integer
                     TimeGraphState timeGraphState = new TimeGraphState(startTime, duration, (int) (s / FREQUENCY_MULTIPLIER), String.valueOf(FREQUENCY_FORMATTER.format(s)));
-                    addToStateList(eventList, timeGraphState, key, predicates, monitor);
+                    applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
                 } else {
                     ITimeGraphState timeGraphState = new TimeGraphState(startTime, duration, Integer.MIN_VALUE);
-                    addToStateList(eventList, timeGraphState, key, predicates, monitor);
+                    applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
                 }
             }
             rows.add(new TimeGraphRowModel(idToQuark.getKey(), eventList));
@@ -667,13 +670,18 @@
     }
 
     @Override
-    public @NonNull Map<@NonNull String, @NonNull String> getFilterInput(@NonNull SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
-        Map<@NonNull String, @NonNull String> inputs = super.getFilterInput(filter, monitor);
+    public @NonNull Multimap<@NonNull String, @NonNull String> getFilterData(long entryId, long time, @Nullable IProgressMonitor monitor) {
+        Multimap<@NonNull String, @NonNull String> data = HashMultimap.create();
+        data.putAll(super.getFilterData(entryId, time, monitor));
+
+        SelectionTimeQueryFilter filter = new SelectionTimeQueryFilter(Collections.singletonList(time), Collections.singleton(Objects.requireNonNull(entryId)));
         TmfModelResponse<Map<String, String>> response = fetchTooltip(filter, monitor);
         Map<@NonNull String, @NonNull String> model = response.getModel();
         if (model != null) {
-            inputs.putAll(model);
+            for (Entry<String, String> entry : model.entrySet()) {
+                data.put(entry.getKey(), entry.getValue());
+            }
         }
-        return inputs;
+        return data;
     }
 }
diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/threadstatus/ThreadEntryModel.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/threadstatus/ThreadEntryModel.java
index fff04f5..2a9148a 100644
--- a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/threadstatus/ThreadEntryModel.java
+++ b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/threadstatus/ThreadEntryModel.java
@@ -10,7 +10,7 @@
 package org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus;
 
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.tracecompass.tmf.core.model.IFilterableDataModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.IElementResolver;
 import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
 
 import com.google.common.collect.HashMultimap;
@@ -21,7 +21,7 @@
  *
  * @author Simon Delisle
  */
-public class ThreadEntryModel extends TimeGraphEntryModel implements IFilterableDataModel {
+public class ThreadEntryModel extends TimeGraphEntryModel implements IElementResolver {
 
     /**
      * {@link ThreadEntryModel} builder, we use this to be able to reassign
diff --git a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/threadstatus/ThreadStatusDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/threadstatus/ThreadStatusDataProvider.java
index 36edb01..3ce26ce 100644
--- a/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/threadstatus/ThreadStatusDataProvider.java
+++ b/analysis/org.eclipse.tracecompass.analysis.os.linux.core/src/org/eclipse/tracecompass/internal/analysis/os/linux/core/threadstatus/ThreadStatusDataProvider.java
@@ -63,6 +63,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import com.google.common.collect.TreeMultimap;
 
@@ -386,7 +387,7 @@
             return new TmfModelResponse<>(null, ITmfResponse.Status.FAILED, String.valueOf(e.getMessage()));
         }
 
-        Map<@NonNull Integer, @NonNull Predicate< @NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        Map<@NonNull Integer, @NonNull Predicate< @NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         if (filter instanceof TimeGraphStateQueryFilter) {
             TimeGraphStateQueryFilter timeEventFilter = (TimeGraphStateQueryFilter) filter;
             predicates.putAll(computeRegexPredicate(timeEventFilter));
@@ -404,7 +405,7 @@
             states.forEach(i -> {
                 ITimeGraphState timegraphState = createTimeGraphState(i, syscalls);
                 Long key = Objects.requireNonNull(entry.getKey());
-                addToStateList(eventList, timegraphState, key, predicates, monitor);
+                applyFilterAndAddState(eventList, timegraphState, key, predicates, monitor);
             });
             rows.add(new TimeGraphRowModel(entry.getKey(), eventList));
         }
diff --git a/analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/CallStackDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/CallStackDataProvider.java
index 9aa23f4..04db1d8 100644
--- a/analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/CallStackDataProvider.java
+++ b/analysis/org.eclipse.tracecompass.analysis.profiling.core/src/org/eclipse/tracecompass/internal/analysis/profiling/core/callstack/provider/CallStackDataProvider.java
@@ -51,6 +51,7 @@
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
 
 /**
  * Call Stack Data Provider
@@ -235,7 +236,7 @@
         }
         subMonitor.worked(1);
 
-        Map<@NonNull Integer, @NonNull Predicate<@NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         if (filter instanceof TimeGraphStateQueryFilter) {
             TimeGraphStateQueryFilter timeEventFilter = (TimeGraphStateQueryFilter) filter;
             predicates.putAll(computeRegexPredicate(timeEventFilter));
@@ -251,7 +252,7 @@
             List<ITimeGraphState> eventList = new ArrayList<>(states.size());
             states.forEach(state -> {
                 ITimeGraphState timeGraphState = createTimeGraphState(state);
-                addToStateList(eventList, timeGraphState, key, predicates, monitor);
+                applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
             });
             eventList.sort(Comparator.comparingLong(ITimeGraphState::getStartTime));
             rows.add(new TimeGraphRowModel(entry.getKey(), eventList));
diff --git a/analysis/org.eclipse.tracecompass.analysis.timing.core/src/org/eclipse/tracecompass/internal/analysis/timing/core/segmentstore/SegmentStoreScatterDataProvider.java b/analysis/org.eclipse.tracecompass.analysis.timing.core/src/org/eclipse/tracecompass/internal/analysis/timing/core/segmentstore/SegmentStoreScatterDataProvider.java
index 3ec74ed..66c549e 100644
--- a/analysis/org.eclipse.tracecompass.analysis.timing.core/src/org/eclipse/tracecompass/internal/analysis/timing/core/segmentstore/SegmentStoreScatterDataProvider.java
+++ b/analysis/org.eclipse.tracecompass.analysis.timing.core/src/org/eclipse/tracecompass/internal/analysis/timing/core/segmentstore/SegmentStoreScatterDataProvider.java
@@ -54,10 +54,12 @@
 
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
 import com.google.common.primitives.Doubles;
 import com.google.common.primitives.Ints;
 import com.google.common.primitives.Longs;
@@ -292,7 +294,7 @@
             return TmfXyResponseFactory.createFailedResponse(Objects.requireNonNull(Messages.SegmentStoreDataProvider_SegmentNotAvailable));
         }
 
-        Map<@NonNull Integer, @NonNull Predicate<@NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         if (filter instanceof IRegexQuery) {
             IRegexQuery regexFilter = (IRegexQuery) filter;
             predicates.putAll(computeRegexPredicate(regexFilter));
@@ -361,18 +363,18 @@
      * @param monitor
      *            The progress monitor
      */
-    private void addPoint(Series series, ISegment segment, Map<Integer, Predicate<Map<String, String>>> predicates, @Nullable IProgressMonitor monitor) {
+    private void addPoint(Series series, ISegment segment, Map<Integer, Predicate<Multimap<String, String>>> predicates, @Nullable IProgressMonitor monitor) {
 
         if (!predicates.isEmpty()) {
 
             // Get the filter external input data
-            Map<@NonNull String, @NonNull String> input = getFilterInput(segment);
+            Multimap<@NonNull String, @NonNull String> input = getFilterInput(segment);
 
             // Test each predicates and set the status of the property associated to the
             // predicate
             int mask = 0;
-            for (Map.Entry<Integer, Predicate<Map<String, String>>> mapEntry : predicates.entrySet()) {
-                Predicate<Map<String, String>> value = Objects.requireNonNull(mapEntry.getValue());
+            for (Map.Entry<Integer, Predicate<Multimap<String, String>>> mapEntry : predicates.entrySet()) {
+                Predicate<Multimap<String, String>> value = Objects.requireNonNull(mapEntry.getValue());
                 boolean status = value.test(input);
                 Integer property = Objects.requireNonNull(mapEntry.getKey());
                 if (status && property != IFilterProperty.DIMMED) {
@@ -391,8 +393,8 @@
         }
     }
 
-    private Map<String, String> getFilterInput(ISegment segment) {
-        Map<String, String> map = new HashMap<>();
+    private Multimap<String, String> getFilterInput(ISegment segment) {
+        Multimap<String, String> map = HashMultimap.create();
         for(ISegmentAspect aspect : fProvider.getSegmentAspects()) {
             Object resolve = aspect.resolve(segment);
             if (resolve != null) {
diff --git a/analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/analysis/timing/ui/views/segmentstore/table/AbstractSegmentStoreTableViewer.java b/analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/analysis/timing/ui/views/segmentstore/table/AbstractSegmentStoreTableViewer.java
index bd3d2d0..a8c1d59 100644
--- a/analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/analysis/timing/ui/views/segmentstore/table/AbstractSegmentStoreTableViewer.java
+++ b/analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/analysis/timing/ui/views/segmentstore/table/AbstractSegmentStoreTableViewer.java
@@ -21,6 +21,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.function.Predicate;
 
@@ -72,6 +73,7 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Multimap;
 
 /**
@@ -380,18 +382,18 @@
     /**
      * Compute the predicate for every property regexes
      *
-     * @param queryFilter
-     *            The query filter holding the regexes
      * @return A map of time event filters predicate by property
      * @since 3.1
+     * @deprecated Use {@link #generateRegexPredicate()}
      */
+    @Deprecated
     protected Map<Integer, Predicate<@NonNull Map<@NonNull String, @NonNull String>>> computeRegexPredicate() {
         Multimap<@NonNull Integer, @NonNull String> regexes = getRegexes();
         Map<@NonNull Integer, @NonNull Predicate<@NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         for (Map.Entry<Integer, Collection<String>> entry : regexes.asMap().entrySet()) {
             String regex = IFilterStrings.mergeFilters(entry.getValue());
             FilterCu cu = FilterCu.compile(regex);
-            Predicate<@NonNull Map<@NonNull String, @NonNull String>> predicate = cu != null ? cu.generate() : null;
+            Predicate<@NonNull Map<@NonNull String, @NonNull String>> predicate = cu != null ? multiToMapPredicate(cu.generate()) : null;
             if (predicate != null) {
                 predicates.put(entry.getKey(), predicate);
             }
@@ -399,6 +401,37 @@
         return predicates;
     }
 
+    private static Predicate<@NonNull Map<@NonNull String, @NonNull String>> multiToMapPredicate(@NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>> predicate) {
+        return new Predicate<@NonNull Map<@NonNull String, @NonNull String>> () {
+
+            @Override
+            public boolean test(@NonNull Map<@NonNull String, @NonNull String> arg0) {
+                return predicate.test(ImmutableMultimap.copyOf(arg0.entrySet()));
+            }
+
+        };
+    }
+
+    /**
+     * Generate the predicate for every property from the regexes
+     *
+     * @return A map of predicate by property
+     * @since 4.0
+     */
+    protected Map<Integer, Predicate<Multimap<String, String>>> generateRegexPredicate() {
+        Multimap<Integer, String> regexes = getRegexes();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        for (Entry<Integer, Collection<String>> entry : regexes.asMap().entrySet()) {
+            String regex = IFilterStrings.mergeFilters(entry.getValue());
+            FilterCu cu = FilterCu.compile(regex);
+            Predicate<@NonNull Multimap<@NonNull String, @NonNull String>> predicate = cu != null ? cu.generate() : null;
+                if (predicate != null) {
+                    predicates.put(entry.getKey(), predicate);
+                }
+        }
+        return predicates;
+    }
+
     /**
      * Returns the segment store provider
      *
diff --git a/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/module/XmlTimeGraphDataProvider.java b/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/module/XmlTimeGraphDataProvider.java
index 4c4495a..542b0fa 100644
--- a/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/module/XmlTimeGraphDataProvider.java
+++ b/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/module/XmlTimeGraphDataProvider.java
@@ -63,6 +63,7 @@
 
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Table;
 
 /**
@@ -361,7 +362,7 @@
 
     private @NonNull Collection<@NonNull ITimeGraphRowModel> createRows(ITmfStateSystem ss, Map<Integer, Long> idToDisplayQuark,
             long[] timesRequested, SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
-        Map<@NonNull Integer, @NonNull Predicate<@NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         if (filter instanceof TimeGraphStateQueryFilter) {
             TimeGraphStateQueryFilter timeEventFilter = (TimeGraphStateQueryFilter) filter;
             predicates.putAll(computeRegexPredicate(timeEventFilter));
@@ -380,7 +381,7 @@
             if (row != null) {
                 List<@NonNull ITimeGraphState> states = row.getStates();
                 ITimeGraphState timeGraphState = getStateFromInterval(interval, currentEndTime);
-                addToStateList(states, timeGraphState, row.getEntryID(), predicates, monitor);
+                applyFilterAndAddState(states, timeGraphState, row.getEntryID(), predicates, monitor);
             }
         }
         for (ITimeGraphRowModel model : quarkToRow.values()) {
diff --git a/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/output/DataDrivenTimeGraphDataProvider.java b/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/output/DataDrivenTimeGraphDataProvider.java
index 6156838..cb345bf 100644
--- a/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/output/DataDrivenTimeGraphDataProvider.java
+++ b/tmf/org.eclipse.tracecompass.tmf.analysis.xml.core/src/org/eclipse/tracecompass/internal/tmf/analysis/xml/core/output/DataDrivenTimeGraphDataProvider.java
@@ -50,6 +50,7 @@
 import org.eclipse.tracecompass.tmf.core.util.Pair;
 
 import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Table;
 
 /**
@@ -154,7 +155,7 @@
 
     private Collection<ITimeGraphRowModel> createRows(ITmfStateSystem ss, Map<Integer, Long> idToDisplayQuark,
             long[] timesRequested, SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
-        Map<@NonNull Integer, @NonNull Predicate<@NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         if (filter instanceof TimeGraphStateQueryFilter) {
             TimeGraphStateQueryFilter timeEventFilter = (TimeGraphStateQueryFilter) filter;
             predicates.putAll(computeRegexPredicate(timeEventFilter));
@@ -173,7 +174,7 @@
             if (row != null) {
                 List<@NonNull ITimeGraphState> states = row.getStates();
                 ITimeGraphState timeGraphState = getStateFromInterval(interval, currentEndTime);
-                addToStateList(states, timeGraphState, row.getEntryID(), predicates, monitor);
+                applyFilterAndAddState(states, timeGraphState, row.getEntryID(), predicates, monitor);
             }
         }
         for (ITimeGraphRowModel model : quarkToRow.values()) {
diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/TmfFilterHelperTest.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/TmfFilterHelperTest.java
index 337c8bd..6e13166 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/TmfFilterHelperTest.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/TmfFilterHelperTest.java
@@ -15,7 +15,6 @@
 import static org.junit.Assert.assertTrue;
 
 import java.util.Collections;
-import java.util.Map;
 import java.util.function.Predicate;
 
 import org.eclipse.jdt.annotation.NonNull;
@@ -48,8 +47,9 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
 
 /**
  * Test the {@link TmfFilterHelper} class to convert event filter to/from
@@ -85,9 +85,9 @@
     private TmfEvent fEvent2 = new TmfEvent(STUB_TRACE, 1, TmfTimestamp.fromNanos(2), EVENT_TYPE2, fContent2);
     private TmfEvent fEvent3 = new TmfEvent(STUB_TRACE, 2, TmfTimestamp.fromNanos(3), EVENT_TYPE3, fContent3);
 
-    private Map<String, String> fObjectMap1 = ImmutableMap.of(FIELD1_NAME, FIELD1_VALUE1, TmfBaseAspects.getEventTypeAspect().getName(), EVENT_NAME1);
-    private Map<String, String> fObjectMap2 = ImmutableMap.of(FIELD2_NAME, FIELD2_VALUE1, TmfBaseAspects.getEventTypeAspect().getName(), EVENT_NAME2);
-    private Map<String, String> fObjectMap3 = ImmutableMap.of(FIELD1_NAME, FIELD1_VALUE2, FIELD2_NAME, FIELD2_VALUE2, TmfBaseAspects.getEventTypeAspect().getName(), EVENT_NAME3);
+    private Multimap<String, String> fObjectMap1 = ImmutableMultimap.of(FIELD1_NAME, FIELD1_VALUE1, TmfBaseAspects.getEventTypeAspect().getName(), EVENT_NAME1);
+    private Multimap<String, String> fObjectMap2 = ImmutableMultimap.of(FIELD2_NAME, FIELD2_VALUE1, TmfBaseAspects.getEventTypeAspect().getName(), EVENT_NAME2);
+    private Multimap<String, String> fObjectMap3 = ImmutableMultimap.of(FIELD1_NAME, FIELD1_VALUE2, FIELD2_NAME, FIELD2_VALUE2, TmfBaseAspects.getEventTypeAspect().getName(), EVENT_NAME3);
 
     /**
      * Initialize the trace
@@ -116,12 +116,12 @@
         return filter;
     }
 
-    private static Predicate<Map<String, String>> getRegex(ITmfFilter filter, String expected) {
+    private static Predicate<Multimap<String, String>> getRegex(ITmfFilter filter, String expected) {
         String regex = TmfFilterHelper.getRegexFromFilter(filter);
         assertEquals(expected, regex);
         FilterCu compile = FilterCu.compile(regex);
         assertNotNull(compile);
-        Predicate<Map<String, String>> predicate = compile.generate();
+        Predicate<Multimap<String, String>> predicate = compile.generate();
         return predicate;
     }
 
@@ -484,7 +484,7 @@
         compareFilter.setValue(FIELD2_VALUE1);
         compareFilter.setResult(1);
 
-        Predicate<Map<String, String>> predicate = getRegex(compareFilter, expected);
+        Predicate<Multimap<String, String>> predicate = getRegex(compareFilter, expected);
         assertFalse(predicate.test(fObjectMap1));
         assertFalse(predicate.test(fObjectMap2));
         assertTrue(predicate.test(fObjectMap3));
@@ -533,7 +533,7 @@
         containsFilter.setEventAspect(aspect);
         containsFilter.setValue("other");
 
-        Predicate<Map<String, String>> predicate = getRegex(containsFilter, expected);
+        Predicate<Multimap<String, String>> predicate = getRegex(containsFilter, expected);
         assertFalse(predicate.test(fObjectMap1));
         assertFalse(predicate.test(fObjectMap2));
         assertTrue(predicate.test(fObjectMap3));
@@ -559,7 +559,7 @@
         equalsFilter.setEventAspect(aspect);
         equalsFilter.setValue(EVENT_NAME1);
 
-        Predicate<Map<String, String>> predicate = getRegex(equalsFilter, expected);
+        Predicate<Multimap<String, String>> predicate = getRegex(equalsFilter, expected);
         assertTrue(predicate.test(fObjectMap1));
         assertFalse(predicate.test(fObjectMap2));
         assertFalse(predicate.test(fObjectMap3));
@@ -585,7 +585,7 @@
         matchesFilter.setEventAspect(aspect);
         matchesFilter.setRegex(".*other.*");
 
-        Predicate<Map<String, String>> predicate = getRegex(matchesFilter, expected);
+        Predicate<Multimap<String, String>> predicate = getRegex(matchesFilter, expected);
         assertFalse(predicate.test(fObjectMap1));
         assertFalse(predicate.test(fObjectMap2));
         assertTrue(predicate.test(fObjectMap3));
@@ -636,7 +636,7 @@
         andFilter.addChild(matchesFilter1);
         andFilter.addChild(matchesFilter2);
 
-        Predicate<Map<String, String>> predicate = getRegex(andFilter, expected);
+        Predicate<Multimap<String, String>> predicate = getRegex(andFilter, expected);
         assertFalse(predicate.test(fObjectMap1));
         assertFalse(predicate.test(fObjectMap2));
         assertTrue(predicate.test(fObjectMap3));
@@ -671,7 +671,7 @@
         orFilter.addChild(matchesFilter1);
         orFilter.addChild(matchesFilter2);
 
-        Predicate<Map<String, String>> predicate = getRegex(orFilter, expected);
+        Predicate<Multimap<String, String>> predicate = getRegex(orFilter, expected);
         assertTrue(predicate.test(fObjectMap1));
         assertFalse(predicate.test(fObjectMap2));
         assertTrue(predicate.test(fObjectMap3));
diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/parser/ElementResolverFilterTest.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/parser/ElementResolverFilterTest.java
index 6b8197e..0b14645 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/parser/ElementResolverFilterTest.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/parser/ElementResolverFilterTest.java
@@ -13,13 +13,13 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Map;
 import java.util.function.Predicate;
 
 import org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser.FilterCu;
 import org.junit.Test;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
 /**
  * Test suite for the {@link FilterCu}
@@ -29,184 +29,201 @@
 @SuppressWarnings("javadoc")
 public class ElementResolverFilterTest {
 
-    private static final ElementResolverStub ELEMENT = new ElementResolverStub(ImmutableMap.of("label", "elementLabel", "key0", "value0", "key1", "value1", "key 2", "value2", "key3", "10"));
+    private static final ElementResolverStub ELEMENT = new ElementResolverStub(ImmutableMultimap.of("label", "elementLabel", "key0", "value0", "key0", "some other", "key 2", "value2", "key3", "10"));
 
     @Test
     public void testRegex() {
         //Test a constant string
         FilterCu cu = FilterCu.compile("Label");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         //Test an unmatched constant string
         cu = FilterCu.compile("fail");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
 
         //Test a regex
         cu = FilterCu.compile("0$");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         //Test a regex
         cu = FilterCu.compile("a.*l");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         //Test an unmatched regex
         cu = FilterCu.compile("y$");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testLogicalOperator() {
         FilterCu cu = FilterCu.compile("Label && value");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("label && fail");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("Label || absent");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("absent || fail");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testEqualsOperator() {
         FilterCu cu = FilterCu.compile("label == elementLabel");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("label == fail");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testNotEqualsOperator() {
         FilterCu cu = FilterCu.compile("label != fail");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("label != elementLabel");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testNotOperator() {
         FilterCu cu = FilterCu.compile("!fail");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("!elementLabel");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testContainsOperator() {
         FilterCu cu = FilterCu.compile("label contains element");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("label contains value");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testPresentOperator() {
-        FilterCu cu = FilterCu.compile("key1 present");
+        FilterCu cu = FilterCu.compile("key0 present");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("invalidKey present");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testMatchesOperator() {
-        FilterCu cu = FilterCu.compile("key1 matches v.*ue");
+        FilterCu cu = FilterCu.compile("key0 matches v.*ue");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
-        cu = FilterCu.compile("key1 matches v.*ue$");
+        // Test with a second value of key0
+        cu = FilterCu.compile("key0 matches o.*er");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
+
+        cu = FilterCu.compile("key0 matches v.*ue$");
+        assertNotNull(cu);
+        predicate = cu.generate();
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile(" \"key 2\" matches value2");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testGreaterThanOperator() {
         FilterCu cu = FilterCu.compile("key3 > 9");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("key3 > 10");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testLessThanOperator() {
         FilterCu cu = FilterCu.compile("key3 < 11");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
         cu = FilterCu.compile("key3 < 10");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
     public void testComplexFilter() {
-        FilterCu cu = FilterCu.compile("(key1 matches v.*ue) && Label");
+        FilterCu cu = FilterCu.compile("(key0 matches v.*ue) && Label");
         assertNotNull(cu);
-        Predicate<Map<String, String>> predicate = cu.generate();
-        assertTrue(predicate.test(ELEMENT.computeData()));
+        Predicate<Multimap<String, String>> predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
 
-        cu = FilterCu.compile("!(key1 matches v.*ue) && Label");
+        cu = FilterCu.compile("!(key0 matches v.*ue) && Label");
         assertNotNull(cu);
         predicate = cu.generate();
-        assertFalse(predicate.test(ELEMENT.computeData()));
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
+
+        // Filter with multiple keys
+        cu = FilterCu.compile("(key0 matches v.*ue) && (key0 matches \"some other\")");
+        assertNotNull(cu);
+        predicate = cu.generate();
+        assertTrue(predicate.test(ELEMENT.getMetadata()));
+
+        cu = FilterCu.compile("(key0 matches v.*ue) && (key0 matches \"invalid\")");
+        assertNotNull(cu);
+        predicate = cu.generate();
+        assertFalse(predicate.test(ELEMENT.getMetadata()));
     }
 
     @Test
diff --git a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/parser/ElementResolverStub.java b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/parser/ElementResolverStub.java
index 4d7c5bf..2b4d20b 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/parser/ElementResolverStub.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core.tests/src/org/eclipse/tracecompass/tmf/core/tests/filter/parser/ElementResolverStub.java
@@ -1,11 +1,13 @@
 package org.eclipse.tracecompass.tmf.core.tests.filter.parser;
 
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.Map;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.tmf.core.model.timegraph.IElementResolver;
 
+import com.google.common.collect.Multimap;
+
 /**
  * Implements an {@link IElementResolver} for test
  *
@@ -13,7 +15,7 @@
  *
  */
 public class ElementResolverStub implements IElementResolver {
-    private @NonNull Map<@NonNull String, @NonNull String> fData = new HashMap<>();
+    private final @NonNull Multimap<@NonNull String, @NonNull String> fData;
 
     /**
      * Constructor
@@ -21,13 +23,19 @@
      * @param data
      *            The data to filter on
      */
-    public ElementResolverStub(@NonNull Map<@NonNull String, @NonNull String> data) {
-        fData.putAll(data);
+    public ElementResolverStub(@NonNull Multimap<@NonNull String, @NonNull String> data) {
+        fData = data;
     }
 
     @Override
-    public @NonNull Map<@NonNull String, @NonNull String> computeData() {
+    public @NonNull Multimap<@NonNull String, @NonNull String> getMetadata() {
         return fData;
     }
 
+    @Deprecated
+    @Override
+    public @NonNull Map<@NonNull String, @NonNull String> computeData() {
+        return Collections.emptyMap();
+    }
+
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/Filter.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/Filter.java
index d36f832..f6c9789 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/Filter.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/Filter.java
@@ -8,16 +8,17 @@
  *******************************************************************************/
 package org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser;
 
-import java.util.Map;
 import java.util.function.Predicate;
 
+import com.google.common.collect.Multimap;
+
 /**
  * Item filter runtime object
  *
  * @author Jean-Christian Kouame
  *
  */
-public class Filter implements Predicate<Map<String, String>> {
+public class Filter implements Predicate<Multimap<String, String>> {
 
     private Iterable<FilterExpression> fExpressions;
 
@@ -32,7 +33,7 @@
     }
 
     @Override
-    public boolean test(Map<String, String> data) {
+    public boolean test(Multimap<String, String> data) {
         for (FilterExpression expression : fExpressions) {
             if (!expression.test(data)) {
                 return false;
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterCu.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterCu.java
index 107da01..5a005af 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterCu.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterCu.java
@@ -10,7 +10,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.function.Predicate;
 
@@ -27,6 +26,7 @@
 import org.eclipse.tracecompass.tmf.filter.parser.FilterParserParser.parse_return;
 
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
 
 /**
  * Compilation unit for a time event filter
@@ -123,7 +123,7 @@
      *
      * @return a filter item runtime object
      */
-    public Predicate<Map<String, String>> generate() {
+    public Predicate<Multimap<String, String>> generate() {
         Iterable<FilterExpression> expressions = Objects.requireNonNull(Iterables.transform(fExpressions, exp -> exp.generate()));
         return new Filter(expressions);
     }
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterExpression.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterExpression.java
index be7564d..b41e479 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterExpression.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterExpression.java
@@ -8,10 +8,11 @@
  *******************************************************************************/
 package org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser;
 
-import java.util.Map;
 import java.util.Queue;
 import java.util.function.Predicate;
 
+import com.google.common.collect.Multimap;
+
 /**
  * This class implement a filter expression that could be tested against an
  * input
@@ -19,7 +20,7 @@
  * @author Jean-Christian Kouame
  *
  */
-public class FilterExpression implements Predicate<Map<String, String>> {
+public class FilterExpression implements Predicate<Multimap<String, String>> {
 
     private final Queue<Object> fElements;
 
@@ -35,7 +36,7 @@
     }
 
     @Override
-    public boolean test(Map<String, String> data) {
+    public boolean test(Multimap<String, String> data) {
 
         if (fElements.isEmpty()) {
             return false;
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterExpressionNot.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterExpressionNot.java
index 790e071..5591d8e 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterExpressionNot.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterExpressionNot.java
@@ -8,9 +8,10 @@
 *******************************************************************************/
 package org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser;
 
-import java.util.Map;
 import java.util.Queue;
 
+import com.google.common.collect.Multimap;
+
 /**
  * This class implement a filter expression negation that could be tested against an
  * input
@@ -32,7 +33,7 @@
     }
 
     @Override
-    public boolean test(Map<String, String> data) {
+    public boolean test(Multimap<String, String> data) {
         return !super.test(data);
     }
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterNot.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterNot.java
index af49821..58d79f2 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterNot.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterNot.java
@@ -8,7 +8,7 @@
 *******************************************************************************/
 package org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser;
 
-import java.util.Map;
+import com.google.common.collect.Multimap;
 
 /**
  * This class represents a filter expression negation
@@ -29,7 +29,7 @@
     }
 
     @Override
-    public boolean test(Map<String, String> data) {
+    public boolean test(Multimap<String, String> data) {
         return !super.test(data);
     }
 
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterSimpleExpression.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterSimpleExpression.java
index be09b1e..ae35c71 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterSimpleExpression.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterSimpleExpression.java
@@ -8,7 +8,6 @@
 *******************************************************************************/
 package org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser;
 
-import java.util.Map;
 import java.util.Objects;
 import java.util.function.BiPredicate;
 import java.util.function.Predicate;
@@ -16,6 +15,7 @@
 import org.eclipse.jdt.annotation.Nullable;
 
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
 
 /**
  * This class represents a simple filter expression
@@ -23,7 +23,7 @@
  * @author Jean-Christian Kouame
  *
  */
-public class FilterSimpleExpression implements Predicate<Map<String, String>> {
+public class FilterSimpleExpression implements Predicate<Multimap<String, String>> {
 
     private final String fField;
     private final BiPredicate<String, String> fOperator;
@@ -46,9 +46,9 @@
     }
 
     @Override
-    public boolean test(Map<String, String> data) {
+    public boolean test(Multimap<String, String> data) {
         String value = fValue;
-        return Iterables.any(data.entrySet(), entry -> (fField.equals("*") ||  //$NON-NLS-1$
+        return Iterables.any(data.entries(), entry -> (fField.equals("*") ||  //$NON-NLS-1$
                 Objects.requireNonNull(entry.getKey()).equals(fField) ||
                 Objects.requireNonNull(entry.getKey()).equals("> " + fField)) &&  //$NON-NLS-1$
                 (value == null || fOperator.test(entry.getValue(), value)));
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterSimpleExpressionNot.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterSimpleExpressionNot.java
index 18bcce1..243b9df 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterSimpleExpressionNot.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/provisional/tmf/core/model/filter/parser/FilterSimpleExpressionNot.java
@@ -8,11 +8,12 @@
 *******************************************************************************/
 package org.eclipse.tracecompass.internal.provisional.tmf.core.model.filter.parser;
 
-import java.util.Map;
 import java.util.function.BiPredicate;
 
 import org.eclipse.jdt.annotation.Nullable;
 
+import com.google.common.collect.Multimap;
+
 /**
  * This class represents a simple filter expression negation
  *
@@ -36,7 +37,7 @@
     }
 
     @Override
-    public boolean test(Map<String, String> data) {
+    public boolean test(Multimap<String, String> data) {
         return !super.test(data);
     }
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/AbstractTmfTraceDataProvider.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/AbstractTmfTraceDataProvider.java
index 6304460..fd5a064 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/AbstractTmfTraceDataProvider.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/AbstractTmfTraceDataProvider.java
@@ -61,13 +61,13 @@
      *            The query filter holding the regexes
      * @return A map of time event filters predicate by property
      */
-    protected Map<Integer, Predicate<@NonNull Map<@NonNull String, @NonNull String>>> computeRegexPredicate(IRegexQuery queryFilter) {
+    protected Map<Integer, Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> computeRegexPredicate(IRegexQuery queryFilter) {
         Multimap<@NonNull Integer, @NonNull String> regexes = queryFilter.getRegexes();
-        Map<@NonNull Integer, @NonNull Predicate<@NonNull Map<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
         for (Map.Entry<Integer, Collection<String>> entry : regexes.asMap().entrySet()) {
             String regex = IFilterStrings.mergeFilters(entry.getValue());
             FilterCu cu = FilterCu.compile(regex);
-            Predicate<@NonNull Map<@NonNull String, @NonNull String>> predicate = cu != null ? cu.generate() : null;
+            Predicate<@NonNull Multimap<@NonNull String, @NonNull String>> predicate = cu != null ? cu.generate() : null;
                 if (predicate != null) {
                     predicates.put(entry.getKey(), predicate);
                 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/timegraph/AbstractTimeGraphDataProvider.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/timegraph/AbstractTimeGraphDataProvider.java
index 9bdd5b1..959f51c 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/timegraph/AbstractTimeGraphDataProvider.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/timegraph/AbstractTimeGraphDataProvider.java
@@ -13,6 +13,7 @@
 import java.util.logging.Level;
 
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLog;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils.FlowScopeLogBuilder;
@@ -31,6 +32,9 @@
 import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
 /**
  * Class to abstract {@link ITimeGraphDataProvider} methods and fields. Handles
  * the exceptions that can be thrown by the concrete classes, and logs the time
@@ -89,6 +93,16 @@
         }
     }
 
+    @Override
+    public @NonNull Multimap<@NonNull String, @NonNull String> getFilterData(long entryId, long time, @Nullable IProgressMonitor monitor) {
+        Multimap<@NonNull String, @NonNull String> data = HashMultimap.create();
+        Multimap<@NonNull String, @NonNull String> inputs = ITimeGraphDataProvider.super.getFilterData(entryId, time, monitor);
+
+        data.putAll(inputs);
+        data.putAll(getEntryMetadata(entryId));
+        return data;
+    }
+
     /**
      * Abstract method to be implemented by the providers to return rows. Lets the
      * abstract class handle waiting for {@link ITmfStateSystem} initialization and
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/tree/AbstractTreeDataProvider.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/tree/AbstractTreeDataProvider.java
index 39775f4..d26259a 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/tree/AbstractTreeDataProvider.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/internal/tmf/core/model/tree/AbstractTreeDataProvider.java
@@ -31,6 +31,7 @@
 import org.eclipse.tracecompass.tmf.core.model.CommonStatusMessage;
 import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
 import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.IElementResolver;
 import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataModel;
 import org.eclipse.tracecompass.tmf.core.model.tree.ITmfTreeDataProvider;
 import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
@@ -40,6 +41,8 @@
 
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
 
 /**
  * Class to abstract {@link ITmfTreeDataProvider} methods and fields. Handles
@@ -69,6 +72,7 @@
     private final ReentrantReadWriteLock fLock = new ReentrantReadWriteLock(false);
     private final BiMap<Long, Integer> fIdToQuark = HashBiMap.create();
     private @Nullable TmfModelResponse<List<M>> fCached;
+    private final Map<Long, Multimap<String, String>> fEntryMetadata = new HashMap<>();
 
     /**
      * Constructor
@@ -190,6 +194,11 @@
             /* Don't query empty state system */
             if (ss.getNbAttributes() > 0 && ss.getStartTime() != Long.MIN_VALUE) {
                 tree = getTree(ss, filter, monitor);
+                for (M model : tree) {
+                    if (model instanceof IElementResolver) {
+                        fEntryMetadata.put(model.getId(), ((IElementResolver) model).getMetadata());
+                    }
+                }
             }
             if (complete) {
                 TmfModelResponse<List<M>> response = new TmfModelResponse<>(tree,
@@ -208,6 +217,18 @@
     }
 
     /**
+     * Get the metadata of an entry by ID. This method returns an empty multimap
+     * if there is no metadata
+     *
+     * @param entryId
+     *            The ID of the entry
+     * @return The metadata map for the entry
+     */
+    protected Multimap<String, String> getEntryMetadata(Long entryId) {
+        return fEntryMetadata.getOrDefault(entryId, ImmutableMultimap.of());
+    }
+
+    /**
      * Abstract method to determine if the trees from a provider are cacheable, to
      * let the {@link AbstractTreeDataProvider} handle caching. Should only return
      * true if the tree does not depend on the filter.
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/IFilterableDataModel.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/IFilterableDataModel.java
index d8bdb2b..fb20a54 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/IFilterableDataModel.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/IFilterableDataModel.java
@@ -12,6 +12,8 @@
 import java.util.HashSet;
 import java.util.Set;
 
+import org.eclipse.tracecompass.tmf.core.model.timegraph.IElementResolver;
+
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
 
@@ -20,7 +22,11 @@
  * additional metadata that can be used to filter them
  *
  * @since 4.2
+ * @deprecated This interface is now totally included in
+ *             {@link IElementResolver}. One may just change the implemented
+ *             interface, the methods have the same names.
  */
+@Deprecated
 public interface IFilterableDataModel {
 
     /**
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/IElementResolver.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/IElementResolver.java
index 2bf51fb..d9db7e4 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/IElementResolver.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/IElementResolver.java
@@ -8,10 +8,17 @@
  **********************************************************************/
 package org.eclipse.tracecompass.tmf.core.model.timegraph;
 
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 import org.eclipse.jdt.annotation.NonNull;
 
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+
 /**
  * Element Resolver, can be used to convert an element to a set of key value
  * pairs.
@@ -45,6 +52,53 @@
      * Get available information from an item and return it into a key-value map
      *
      * @return The map of data
+     * @deprecated Use the {@link #getMetadata()} instead
      */
-    Map<@NonNull String, @NonNull String> computeData();
+    @Deprecated
+    default Map<@NonNull String, @NonNull String> computeData() {
+        Map<String, String> map = new HashMap<>();
+        Multimap<String, String> metadata = getMetadata();
+        for (Entry<String, String> entry : metadata.entries()) {
+            map.put(entry.getKey(), entry.getValue());
+        }
+        return map;
+    }
+
+    /**
+     * Get the metadata for this data model. The keys are the names of the
+     * metadata field or aspect. A field may have multiple values associated
+     * with it.
+     *
+     * @return A map of field names to values
+     * @since 5.0
+     */
+    Multimap<@NonNull String, @NonNull String> getMetadata();
+
+    /**
+     * Compare 2 sets of metadata to see if the second intersects the first. 2
+     * sets of metadata are said to intersect if they have at least one key in
+     * common and for each key that they have in common, they have at least one
+     * value in common.
+     *
+     * @param data1
+     *            The first set of metadata to compare
+     * @param data2
+     *            The second set of metadata to compare
+     * @return Whether the 2 metadata sets coincides
+     * @since 5.0
+     */
+    static boolean commonIntersect(Multimap<String, String> data1, Multimap<String, String> data2) {
+        Set<String> commonKeys = new HashSet<>(data1.keySet());
+        commonKeys.retainAll(data2.keySet());
+        if (commonKeys.isEmpty()) {
+            return false;
+        }
+        for (String commonKey : commonKeys) {
+            if (!Iterables.any(data1.get(commonKey), v -> data2.get(commonKey).contains(v))) {
+                return false;
+            }
+        }
+        return true;
+
+    }
 }
\ No newline at end of file
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/ITimeGraphStateFilter.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/ITimeGraphStateFilter.java
index 874ee9d..77c8e07 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/ITimeGraphStateFilter.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/ITimeGraphStateFilter.java
@@ -20,6 +20,10 @@
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
 /**
  * Implements timegraph state filtering support. This interface provide default
  * method to provide the inputs and do the filtering.
@@ -46,7 +50,9 @@
      *            associated predicate test result.
      * @param monitor
      *            The progress monitor
+     * @deprecated Use the {@link #applyFilterAndAddState(List, ITimeGraphState, Long, Multimap, Map, IProgressMonitor)} instead
      */
+    @Deprecated
     default void addToStateList(List<ITimeGraphState> stateList, ITimeGraphState timeGraphState, Long key, Map<Integer, Predicate<Map<String, String>>> predicates, @Nullable IProgressMonitor monitor) {
 
         if (!predicates.isEmpty()) {
@@ -80,6 +86,57 @@
     }
 
     /**
+     * Filter the time graph state and add it to the state list
+     *
+     * @param stateList
+     *            The timegraph state list
+     * @param timeGraphState
+     *            The current timegraph state
+     * @param key
+     *            The timegraph entry model id
+     * @param predicates
+     *            The predicates used to filter the timegraph state. It is a map
+     *            of predicate by property. The value of the property is an
+     *            integer representing a bitmask associated to that property.
+     *            The status of each property will be set for the timegraph
+     *            state according to the associated predicate test result.
+     * @param monitor
+     *            The progress monitor
+     * @since 5.0
+     */
+    default void applyFilterAndAddState(List<ITimeGraphState> stateList, ITimeGraphState timeGraphState, Long key, Map<Integer, Predicate<Multimap<String, String>>> predicates, @Nullable IProgressMonitor monitor) {
+
+        if (!predicates.isEmpty()) {
+            // Get the filter external input data
+            long startTime = timeGraphState.getStartTime();
+            Multimap<@NonNull String, @NonNull String> input = HashMultimap.create();
+            input.putAll(getFilterData(key, startTime, monitor));
+            input.putAll(timeGraphState.getMetadata());
+
+            // Test each predicates and set the status of the property associated to the
+            // predicate
+            for (Map.Entry<Integer, Predicate<Multimap<String, String>>> mapEntry : predicates.entrySet()) {
+                Predicate<Multimap<String, String>> value = Objects.requireNonNull(mapEntry.getValue());
+                boolean status = value.test(input);
+                Integer property = Objects.requireNonNull(mapEntry.getKey());
+                if (property == IFilterProperty.DIMMED || property == IFilterProperty.EXCLUDE) {
+                    timeGraphState.setProperty(property, !status);
+                } else {
+                    timeGraphState.setProperty(property, status);
+                }
+            }
+        }
+
+        if (timeGraphState.isPropertyActive(IFilterProperty.EXCLUDE)) {
+            TimeGraphState timeGraphState2 = new TimeGraphState(timeGraphState.getStartTime(), timeGraphState.getDuration(), Integer.MIN_VALUE);
+            timeGraphState2.setActiveProperties(timeGraphState.getActiveProperties());
+            stateList.add(timeGraphState2);
+        } else {
+            stateList.add(timeGraphState);
+        }
+    }
+
+    /**
      * Get input data used for filtering
      *
      * @param filter
@@ -87,8 +144,26 @@
      * @param monitor
      *            The progress monitor
      * @return The map of input data
+     * @deprecated Use the {@link #getFilterData(long, long, IProgressMonitor)} instead
      */
+    @Deprecated
     default Map<String, String> getFilterInput(SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
         return new HashMap<>();
     }
+
+    /**
+     * Get input data used for filtering
+     *
+     * @param entryId
+     *            The ID of the entry
+     * @param time
+     *            The time at which to get data
+     * @param monitor
+     *            The progress monitor
+     * @return The map of input data
+     * @since 5.0
+     */
+    default Multimap<String, String> getFilterData(long entryId, long time, @Nullable IProgressMonitor monitor) {
+        return ImmutableMultimap.of();
+    }
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/TimeGraphState.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/TimeGraphState.java
index 6575bc1..90047cf 100644
--- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/TimeGraphState.java
+++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/model/timegraph/TimeGraphState.java
@@ -14,6 +14,9 @@
 
 import org.eclipse.jdt.annotation.Nullable;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
 /**
  * Implementation of {@link ITimeGraphState}
  *
@@ -87,8 +90,8 @@
     }
 
     @Override
-    public Map<String, String> computeData() {
-        Map<String, String> toTest = new HashMap<>();
+    public Multimap<String, String> getMetadata() {
+        Multimap<String, String> toTest = HashMultimap.create();
         String label = getLabel();
         if (label != null) {
             toTest.put(IElementResolver.LABEL_KEY, label);
@@ -105,4 +108,15 @@
     public void setActiveProperties(int activeProperties) {
         fActiveProperties = activeProperties;
     }
+
+    @Deprecated
+    @Override
+    public Map<String, String> computeData() {
+        Map<String, String> toTest = new HashMap<>();
+        String label = getLabel();
+        if (label != null) {
+            toTest.put(IElementResolver.LABEL_KEY, label);
+        }
+        return toTest;
+    }
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartEvent.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartEvent.java
index 994c261..879ba2a 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartEvent.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timechart/TimeChartEvent.java
@@ -15,12 +15,16 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
 import org.eclipse.tracecompass.tmf.ui.views.colors.ColorSettingsManager;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
 
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
 /**
  * Event in the time chart view
  *
@@ -386,4 +390,9 @@
             }
         }
     }
+
+    @Override
+    public @NonNull Multimap<@NonNull String, @NonNull String> getMetadata() {
+        return ImmutableMultimap.of();
+    }
 }
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java
index 7b75aff..0144b34 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/views/timegraph/AbstractTimeGraphView.java
@@ -113,7 +113,7 @@
 import org.eclipse.tracecompass.internal.tmf.ui.views.ITmfTimeZoomProvider;
 import org.eclipse.tracecompass.internal.tmf.ui.views.ITmfZoomToSelectionProvider;
 import org.eclipse.tracecompass.internal.tmf.ui.views.timegraph.TimeEventFilterDialog;
-import org.eclipse.tracecompass.tmf.core.model.IFilterableDataModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.IElementResolver;
 import org.eclipse.tracecompass.tmf.core.model.timegraph.IFilterProperty;
 import org.eclipse.tracecompass.tmf.core.resources.ITmfMarker;
 import org.eclipse.tracecompass.tmf.core.signal.TmfDataModelSelectedSignal;
@@ -186,6 +186,7 @@
 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
 
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Multimap;
@@ -385,8 +386,8 @@
         @Override
         public void selectionChanged(TimeGraphSelectionEvent event) {
             ITimeGraphEntry entry = event.getSelection();
-            if (entry instanceof IFilterableDataModel) {
-                Multimap<@NonNull String, @NonNull String> metadata = ((IFilterableDataModel) entry).getMetadata();
+            if (entry instanceof IElementResolver) {
+                Multimap<@NonNull String, @NonNull String> metadata = ((IElementResolver) entry).getMetadata();
                 if (!metadata.isEmpty()) {
                     broadcast(new TmfDataModelSelectedSignal(AbstractTimeGraphView.this, metadata));
                 }
@@ -794,7 +795,9 @@
      *
      * @return A map of time event filters predicate by property
      * @since 4.0
+     * @deprecated Use {@link #generateRegexPredicate()}
      */
+    @Deprecated
     @NonNullByDefault
     protected Map<Integer, Predicate<Map<String, String>>> computeRegexPredicate() {
         Multimap<Integer, String> regexes = getRegexes();
@@ -802,7 +805,32 @@
         for (Entry<Integer, Collection<String>> entry : regexes.asMap().entrySet()) {
             String regex = IFilterStrings.mergeFilters(entry.getValue());
             FilterCu cu = FilterCu.compile(regex);
-            Predicate<@NonNull Map<@NonNull String, @NonNull String>> predicate = cu != null ? cu.generate() : null;
+            Predicate<@NonNull Map<@NonNull String, @NonNull String>> predicate = cu != null ? multiToMapPredicate(cu.generate()) : null;
+                if (predicate != null) {
+                    predicates.put(entry.getKey(), predicate);
+                }
+        }
+        return predicates;
+    }
+
+    private static Predicate<@NonNull Map<@NonNull String, @NonNull String>> multiToMapPredicate(@NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>> predicate) {
+        return map -> predicate.test(Objects.requireNonNull(ImmutableMultimap.copyOf(map.entrySet())));
+    }
+
+    /**
+     * Generate the predicate for every property from the regexes
+     *
+     * @return A map of predicate by property
+     * @since 5.0
+     */
+    @NonNullByDefault
+    protected Map<Integer, Predicate<Multimap<String, String>>> generateRegexPredicate() {
+        Multimap<Integer, String> regexes = getRegexes();
+        Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
+        for (Entry<Integer, Collection<String>> entry : regexes.asMap().entrySet()) {
+            String regex = IFilterStrings.mergeFilters(entry.getValue());
+            FilterCu cu = FilterCu.compile(regex);
+            Predicate<@NonNull Multimap<@NonNull String, @NonNull String>> predicate = cu != null ? cu.generate() : null;
                 if (predicate != null) {
                     predicates.put(entry.getKey(), predicate);
                 }
@@ -2861,8 +2889,8 @@
         Multimap<@NonNull String, @NonNull String> metadata = signal.getMetadata();
         // See if the current selection intersects the metadata
         ITimeGraphEntry selection = getTimeGraphViewer().getSelection();
-        if (selection instanceof IFilterableDataModel &&
-                IFilterableDataModel.commonIntersect(metadata, ((IFilterableDataModel)selection).getMetadata())) {
+        if (selection instanceof IElementResolver &&
+                IElementResolver.commonIntersect(metadata, ((IElementResolver)selection).getMetadata())) {
             return;
         }
         // See if an entry intersects the metadata
@@ -2873,7 +2901,7 @@
         for (TraceEntry traceEntry : Iterables.filter(traceEntries, TraceEntry.class)) {
             Iterable<TimeGraphEntry> unfiltered = Utils.flatten(traceEntry);
             for (TimeGraphEntry entry : unfiltered) {
-                if (IFilterableDataModel.commonIntersect(metadata, entry.getMetadata())) {
+                if (IElementResolver.commonIntersect(metadata, entry.getMetadata())) {
                     getTimeGraphViewer().setSelection(entry, true);
                 }
             }
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/ITimeEvent.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/ITimeEvent.java
index cccdfb4..ca37f9c 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/ITimeEvent.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/ITimeEvent.java
@@ -20,6 +20,9 @@
 import org.eclipse.tracecompass.tmf.core.model.timegraph.IElementResolver;
 import org.eclipse.tracecompass.tmf.core.model.timegraph.IPropertyCollection;
 
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+
 /**
  * Interface for time events, for use in the timegraph view
  *
@@ -99,4 +102,13 @@
 
         return data;
     }
+
+    /**
+     * @since 5.0
+     */
+    @Override
+    default @NonNull Multimap<@NonNull String, @NonNull String> getMetadata() {
+        String entryName = getEntry().getName();
+        return (entryName != null) ? ImmutableMultimap.of(IElementResolver.ENTRY_NAME_KEY, entryName) : ImmutableMultimap.of();
+    }
 }
\ No newline at end of file
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeEvent.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeEvent.java
index 43075c4..474490c 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeEvent.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeEvent.java
@@ -16,7 +16,6 @@
 import java.util.Objects;
 
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.tracecompass.tmf.core.model.IFilterableDataModel;
 import org.eclipse.tracecompass.tmf.core.model.timegraph.IFilterProperty;
 
 import com.google.common.collect.HashMultimap;
@@ -28,7 +27,7 @@
  * @version 1.0
  * @author Patrick Tasse
  */
-public class TimeEvent implements ITimeEvent, IFilterableDataModel {
+public class TimeEvent implements ITimeEvent {
 
     /** TimeGraphEntry matching this time event */
     protected ITimeGraphEntry fEntry;
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeGraphEntry.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeGraphEntry.java
index cc27c46..59d2e69 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeGraphEntry.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/tmf/ui/widgets/timegraph/model/TimeGraphEntry.java
@@ -30,17 +30,17 @@
 import org.eclipse.swt.SWT;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
 import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils;
-import org.eclipse.tracecompass.tmf.core.model.IFilterableDataModel;
+import org.eclipse.tracecompass.tmf.core.model.timegraph.IElementResolver;
 import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphEntryModel;
 import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphEntryModel;
 
-import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Multimap;
 
 /**
  * An entry for use in the time graph views
  */
-public class TimeGraphEntry implements ITimeGraphEntry, IFilterableDataModel {
+public class TimeGraphEntry implements ITimeGraphEntry, IElementResolver {
 
     /**
      * Class to describe on which time range and resolution the zoomed entry list is
@@ -577,10 +577,10 @@
      */
     @Override
     public @NonNull Multimap<@NonNull String, @NonNull String> getMetadata() {
-        if (fModel instanceof IFilterableDataModel) {
-            return ((IFilterableDataModel) fModel).getMetadata();
+        if (fModel instanceof IElementResolver) {
+            return ((IElementResolver) fModel).getMetadata();
         }
-        return HashMultimap.create();
+        return ImmutableMultimap.of();
     }
 
     /**
@@ -612,4 +612,13 @@
         }
         return false;
     }
+
+    @Deprecated
+    @Override
+    public @NonNull Map<@NonNull String, @NonNull String> computeData() {
+        if (fModel instanceof IElementResolver) {
+            return ((IElementResolver) fModel).computeData();
+        }
+        return Collections.emptyMap();
+    }
 }