tmf: Add utility method to search for events matching a Predicate
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / TmfTraceUtils.java
index 8948bca04302e259aa7f5baf29fe6fb89336dd31..084cf22076af7f6fbc1a1f8a99b8cc950bad29c1 100644 (file)
@@ -18,15 +18,21 @@ import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
 
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.common.core.StreamUtils;
 import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider;
 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
 import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
+import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
+
+import com.google.common.collect.Iterables;
 
 /**
  * Utility methods for ITmfTrace's.
@@ -42,8 +48,8 @@ public final class TmfTraceUtils {
     }
 
     /**
-     * Get an analysis module belonging to this trace, with the specified ID and
-     * class.
+     * Return the first result of the first analysis module belonging to this trace or its children,
+     * with the specified ID and class.
      *
      * @param trace
      *            The trace for which you want the modules
@@ -66,23 +72,31 @@ public final class TmfTraceUtils {
     }
 
     /**
-     * Return the analysis modules that are of a given class. Module will be
-     * casted to the requested class.
+     * Return the analysis modules that are of a given class. The modules will be
+     * cast to the requested class. If the trace has children, the childrens modules
+     * are also returned.
      *
      * @param trace
-     *            The trace for which you want the modules
+     *            The trace for which you want the modules, the children trace modules
+     *            are added as well.
      * @param moduleClass
      *            Returned modules must extend this class
      * @return List of modules of class moduleClass
      */
     public static <T> Iterable<@NonNull T> getAnalysisModulesOfClass(ITmfTrace trace, Class<T> moduleClass) {
         Iterable<IAnalysisModule> analysisModules = trace.getAnalysisModules();
-        Set<@NonNull T> modules = new HashSet<>();
+        List<@NonNull T> modules = new ArrayList<>();
         for (IAnalysisModule module : analysisModules) {
             if (moduleClass.isAssignableFrom(module.getClass())) {
                 modules.add(checkNotNull(moduleClass.cast(module)));
             }
         }
+        for (ITmfEventProvider child : trace.getChildren()) {
+            if (child instanceof ITmfTrace) {
+                ITmfTrace childTrace = (ITmfTrace) child;
+                Iterables.addAll(modules, getAnalysisModulesOfClass(childTrace, moduleClass));
+            }
+        }
         return modules;
     }
 
@@ -101,24 +115,19 @@ public final class TmfTraceUtils {
      *         {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
      *         for the event or {@code null} otherwise
      */
-    public static @Nullable <T extends ITmfEventAspect> Object resolveEventAspectOfClassForEvent(
+    public static <T extends ITmfEventAspect<?>> @Nullable Object resolveEventAspectOfClassForEvent(
             ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) {
-        Iterable<ITmfEventAspect> aspects = trace.getEventAspects();
-        for (ITmfEventAspect aspect : aspects) {
-            if (aspectClass.isAssignableFrom(aspect.getClass())) {
-                Object obj = aspect.resolve(event);
-                if (obj != null) {
-                    return obj;
-                }
-            }
-        }
-        return null;
+            return StreamUtils.getStream(trace.getEventAspects())
+                    .filter(aspect -> aspectClass.isAssignableFrom(aspect.getClass()))
+                    .map(aspect -> aspect.resolve(event))
+                    .filter(obj -> obj != null)
+                    .findFirst().orElse(null);
     }
 
     /**
-     * Return the first result of the first aspect that resolves as non null for
-     * the event received in parameter. The result is cast to an Integer if
-     * possible, otherwise null is returned.
+     * Return the first result of the first aspect that resolves as a non-null
+     * Integer for the event received in parameter. If no matching aspects are
+     * found then null is returned.
      *
      * @param trace
      *            The trace for which you want the event aspects
@@ -131,13 +140,14 @@ public final class TmfTraceUtils {
      *         for the event or {@code null} otherwise
      * @since 2.0
      */
-    public static @Nullable <T extends ITmfEventAspect> Integer resolveIntEventAspectOfClassForEvent(
+    public static <T extends ITmfEventAspect<Integer>> @Nullable Integer resolveIntEventAspectOfClassForEvent(
             ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) {
-        Object result = resolveEventAspectOfClassForEvent(trace, aspectClass, event);
-        if (result instanceof Integer) {
-            return (Integer) result;
-        }
-        return null;
+            return StreamUtils.getStream(trace.getEventAspects())
+                .filter(aspect -> aspectClass.isAssignableFrom(aspect.getClass()))
+                /* Enforced by the T parameter bounding */
+                .map(aspect -> (Integer) aspect.resolve(event))
+                .filter(obj -> obj != null)
+                .findFirst().orElse(null);
     }
 
     /**
@@ -151,7 +161,7 @@ public final class TmfTraceUtils {
      * @return true if it is binary else false
      * @throws IOException
      *             if IOException occurs
-     * @since 2.0
+     * @since 1.2
      */
     public static boolean isText(File file) throws IOException {
         try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) {
@@ -167,4 +177,157 @@ public final class TmfTraceUtils {
         }
         return true;
     }
+
+    // ------------------------------------------------------------------------
+    // Event matching methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Retrieve from a trace the next event, from a starting rank, matching the
+     * given predicate.
+     *
+     * @param trace
+     *            The trace
+     * @param startRank
+     *            The rank of the event at which to start searching. Use
+     *            <code>0</code> to search from the start of the trace.
+     * @param predicate
+     *            The predicate to test events against
+     * @return The first event matching the predicate, or null if the end of the
+     *         trace was reached and no event was found
+     */
+    public static @Nullable ITmfEvent getNextEventMatching(ITmfTrace trace, long startRank, Predicate<ITmfEvent> predicate) {
+        /* rank + 1 because we do not want to include the start event itself in the search */
+        EventMatchingRequest req = new EventMatchingRequest(startRank + 1, predicate, false);
+        trace.sendRequest(req);
+        try {
+            req.waitForCompletion();
+        } catch (InterruptedException e) {
+            return null;
+        }
+
+        return req.getFoundEvent();
+    }
+
+    /**
+     * Retrieve from a trace the previous event, from a given rank, matching the
+     * given predicate.
+     *
+     * @param trace
+     *            The trace
+     * @param startRank
+     *            The rank of the event at which to start searching backwards.
+     * @param predicate
+     *            The predicate to test events against
+     * @return The first event found matching the predicate, or null if the
+     *         beginning of the trace was reached and no event was found
+     */
+    public static @Nullable ITmfEvent getPreviousEventMatching(ITmfTrace trace, long startRank, Predicate<ITmfEvent> predicate) {
+        /*
+         * Slightly less straightforward since we unfortunately cannot iterate
+         * backwards on events. Do a series of forward-queries.
+         */
+        final int targetStep = 100;
+
+        /*
+         * If we are close to the beginning of the trace, make sure we only look
+         * for the events before the startRank.
+         */
+        int step = (startRank < targetStep ? (int) startRank : targetStep);
+
+        long currentRank = startRank;
+        try {
+            while (currentRank > 0) {
+                currentRank = Math.max(currentRank - step, 0);
+
+                EventMatchingRequest req = new EventMatchingRequest(currentRank, step, predicate, true);
+                trace.sendRequest(req);
+                req.waitForCompletion();
+
+                ITmfEvent event = req.getFoundEvent();
+                if (event != null) {
+                    /* We found an actual event, return it! */
+                    return event;
+                }
+                /* Keep searching, next loop */
+
+            }
+        } catch (InterruptedException e) {
+            return null;
+        }
+
+        /*
+         * We searched up to the beginning of the trace and didn't find
+         * anything.
+         */
+        return null;
+
+    }
+
+    /**
+     * Event request looking for an event matching a Predicate.
+     */
+    private static class EventMatchingRequest extends TmfEventRequest {
+
+        private final Predicate<ITmfEvent> fPredicate;
+        private final boolean fReturnLast;
+
+        private @Nullable ITmfEvent fFoundEvent = null;
+
+        /**
+         * Basic constructor, will query the trace until the end.
+         *
+         * @param startRank
+         *            The rank at which to start, use 0 for the beginning
+         * @param predicate
+         *            The predicate to test against each event
+         * @param returnLast
+         *            Should we return the last or first event found. If false,
+         *            the request ends as soon as a matching event is found. If
+         *            false, we will go through all events to find a possible
+         *            last-match.
+         */
+        public EventMatchingRequest(long startRank, Predicate<ITmfEvent> predicate, boolean returnLast) {
+            super(ITmfEvent.class, startRank, ALL_DATA, ExecutionType.FOREGROUND);
+            fPredicate = predicate;
+            fReturnLast = returnLast;
+        }
+
+        /**
+         * Basic constructor, will query the trace the limit is reached.
+         *
+         * @param startRank
+         *            The rank at which to start, use 0 for the beginning
+         * @param limit
+         *            The limit on the number of events
+         * @param predicate
+         *            The predicate to test against each event
+         * @param returnLast
+         *            Should we return the last or first event found. If false,
+         *            the request ends as soon as a matching event is found. If
+         *            false, we will go through all events to find a possible
+         *            last-match.
+         */
+        public EventMatchingRequest(long startRank, int limit, Predicate<ITmfEvent> predicate, boolean returnLast) {
+            super(ITmfEvent.class, startRank, limit, ExecutionType.FOREGROUND);
+            fPredicate = predicate;
+            fReturnLast = returnLast;
+        }
+
+        public @Nullable ITmfEvent getFoundEvent() {
+            return fFoundEvent;
+        }
+
+        @Override
+        public void handleData(ITmfEvent event) {
+            super.handleData(event);
+
+            if (fPredicate.test(event)) {
+                fFoundEvent = event;
+                if (!fReturnLast) {
+                    this.done();
+                }
+            }
+        }
+    }
 }
This page took 0.028673 seconds and 5 git commands to generate.