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
CommitLineData
b8585c7c
AM
1/*******************************************************************************
2 * Copyright (c) 2014 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Alexandre Montplaisir - Initial API and implementation
11 *******************************************************************************/
12
13package org.eclipse.tracecompass.tmf.core.trace;
14
aa353506
AM
15import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
16
d26d67f5
BH
17import java.io.BufferedInputStream;
18import java.io.File;
19import java.io.FileInputStream;
20import java.io.IOException;
daf3c7eb
MK
21import java.util.ArrayList;
22import java.util.List;
e4bc4695 23import java.util.function.Predicate;
b8585c7c 24
df2597e0 25import org.eclipse.jdt.annotation.NonNull;
1d83ed07 26import org.eclipse.jdt.annotation.NonNullByDefault;
b8585c7c 27import org.eclipse.jdt.annotation.Nullable;
c15e897d 28import org.eclipse.tracecompass.common.core.StreamUtils;
b8585c7c 29import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
daf3c7eb 30import org.eclipse.tracecompass.tmf.core.component.ITmfEventProvider;
b1aad44e 31import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
35f39420 32import org.eclipse.tracecompass.tmf.core.event.aspect.ITmfEventAspect;
e4bc4695 33import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
b8585c7c 34
daf3c7eb
MK
35import com.google.common.collect.Iterables;
36
b8585c7c
AM
37/**
38 * Utility methods for ITmfTrace's.
39 *
40 * @author Alexandre Montplaisir
41 */
1d83ed07 42@NonNullByDefault
b8585c7c
AM
43public final class TmfTraceUtils {
44
d26d67f5
BH
45 private static final int MAX_NB_BINARY_BYTES = 2048;
46
b1aad44e
GB
47 private TmfTraceUtils() {
48 }
b8585c7c
AM
49
50 /**
daf3c7eb
MK
51 * Return the first result of the first analysis module belonging to this trace or its children,
52 * with the specified ID and class.
b8585c7c
AM
53 *
54 * @param trace
55 * The trace for which you want the modules
56 * @param moduleClass
57 * Returned modules must extend this class
58 * @param id
59 * The ID of the analysis module
60 * @return The analysis module with specified class and ID, or null if no
61 * such module exists.
62 */
63 public static @Nullable <T extends IAnalysisModule> T getAnalysisModuleOfClass(ITmfTrace trace,
64 Class<T> moduleClass, String id) {
65 Iterable<T> modules = getAnalysisModulesOfClass(trace, moduleClass);
66 for (T module : modules) {
67 if (id.equals(module.getId())) {
68 return module;
69 }
70 }
71 return null;
72 }
73
74 /**
daf3c7eb
MK
75 * Return the analysis modules that are of a given class. The modules will be
76 * cast to the requested class. If the trace has children, the childrens modules
77 * are also returned.
b8585c7c
AM
78 *
79 * @param trace
daf3c7eb
MK
80 * The trace for which you want the modules, the children trace modules
81 * are added as well.
b8585c7c
AM
82 * @param moduleClass
83 * Returned modules must extend this class
84 * @return List of modules of class moduleClass
85 */
df2597e0 86 public static <T> Iterable<@NonNull T> getAnalysisModulesOfClass(ITmfTrace trace, Class<T> moduleClass) {
b8585c7c 87 Iterable<IAnalysisModule> analysisModules = trace.getAnalysisModules();
daf3c7eb 88 List<@NonNull T> modules = new ArrayList<>();
b1aad44e 89 for (IAnalysisModule module : analysisModules) {
b8585c7c 90 if (moduleClass.isAssignableFrom(module.getClass())) {
aa353506 91 modules.add(checkNotNull(moduleClass.cast(module)));
b8585c7c
AM
92 }
93 }
daf3c7eb
MK
94 for (ITmfEventProvider child : trace.getChildren()) {
95 if (child instanceof ITmfTrace) {
96 ITmfTrace childTrace = (ITmfTrace) child;
97 Iterables.addAll(modules, getAnalysisModulesOfClass(childTrace, moduleClass));
98 }
99 }
b8585c7c
AM
100 return modules;
101 }
35f39420
AM
102
103 /**
b1aad44e
GB
104 * Return the first result of the first aspect that resolves as non null for
105 * the event received in parameter. If the returned value is not null, it
106 * can be safely cast to the aspect's class proper return type.
35f39420
AM
107 *
108 * @param trace
109 * The trace for which you want the event aspects
110 * @param aspectClass
b1aad44e
GB
111 * The class of the aspect(s) to resolve
112 * @param event
113 * The event for which to get the aspect
114 * @return The first result of the
115 * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
116 * for the event or {@code null} otherwise
35f39420 117 */
c15e897d 118 public static <T extends ITmfEventAspect<?>> @Nullable Object resolveEventAspectOfClassForEvent(
1d83ed07 119 ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) {
c15e897d
AM
120 return StreamUtils.getStream(trace.getEventAspects())
121 .filter(aspect -> aspectClass.isAssignableFrom(aspect.getClass()))
122 .map(aspect -> aspect.resolve(event))
123 .filter(obj -> obj != null)
124 .findFirst().orElse(null);
35f39420 125 }
d26d67f5 126
b3867ecc 127 /**
c15e897d
AM
128 * Return the first result of the first aspect that resolves as a non-null
129 * Integer for the event received in parameter. If no matching aspects are
130 * found then null is returned.
b3867ecc
MAL
131 *
132 * @param trace
133 * The trace for which you want the event aspects
134 * @param aspectClass
135 * The class of the aspect(s) to resolve
136 * @param event
137 * The event for which to get the aspect
138 * @return Integer of the first result of the
139 * {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
140 * for the event or {@code null} otherwise
141 * @since 2.0
142 */
c15e897d 143 public static <T extends ITmfEventAspect<Integer>> @Nullable Integer resolveIntEventAspectOfClassForEvent(
b3867ecc 144 ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) {
c15e897d
AM
145 return StreamUtils.getStream(trace.getEventAspects())
146 .filter(aspect -> aspectClass.isAssignableFrom(aspect.getClass()))
147 /* Enforced by the T parameter bounding */
148 .map(aspect -> (Integer) aspect.resolve(event))
149 .filter(obj -> obj != null)
150 .findFirst().orElse(null);
b3867ecc
MAL
151 }
152
d26d67f5
BH
153 /**
154 * Checks for text file.
155 *
156 * Note that it checks for binary value 0 in the first MAX_NB_BINARY_BYTES
157 * bytes to determine if the file is text.
158 *
159 * @param file
160 * the file to check. Caller has to make sure that file exists.
161 * @return true if it is binary else false
162 * @throws IOException
163 * if IOException occurs
066b02aa 164 * @since 1.2
d26d67f5
BH
165 */
166 public static boolean isText(File file) throws IOException {
167 try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) {
168 int count = 0;
169 int val = bufferedInputStream.read();
170 while ((count < MAX_NB_BINARY_BYTES) && (val >= 0)) {
171 if (val == 0) {
172 return false;
173 }
174 count++;
175 val = bufferedInputStream.read();
176 }
177 }
178 return true;
179 }
e4bc4695
AM
180
181 // ------------------------------------------------------------------------
182 // Event matching methods
183 // ------------------------------------------------------------------------
184
185 /**
186 * Retrieve from a trace the next event, from a starting rank, matching the
187 * given predicate.
188 *
189 * @param trace
190 * The trace
191 * @param startRank
192 * The rank of the event at which to start searching. Use
193 * <code>0</code> to search from the start of the trace.
194 * @param predicate
195 * The predicate to test events against
196 * @return The first event matching the predicate, or null if the end of the
197 * trace was reached and no event was found
198 */
199 public static @Nullable ITmfEvent getNextEventMatching(ITmfTrace trace, long startRank, Predicate<ITmfEvent> predicate) {
200 /* rank + 1 because we do not want to include the start event itself in the search */
201 EventMatchingRequest req = new EventMatchingRequest(startRank + 1, predicate, false);
202 trace.sendRequest(req);
203 try {
204 req.waitForCompletion();
205 } catch (InterruptedException e) {
206 return null;
207 }
208
209 return req.getFoundEvent();
210 }
211
212 /**
213 * Retrieve from a trace the previous event, from a given rank, matching the
214 * given predicate.
215 *
216 * @param trace
217 * The trace
218 * @param startRank
219 * The rank of the event at which to start searching backwards.
220 * @param predicate
221 * The predicate to test events against
222 * @return The first event found matching the predicate, or null if the
223 * beginning of the trace was reached and no event was found
224 */
225 public static @Nullable ITmfEvent getPreviousEventMatching(ITmfTrace trace, long startRank, Predicate<ITmfEvent> predicate) {
226 /*
227 * Slightly less straightforward since we unfortunately cannot iterate
228 * backwards on events. Do a series of forward-queries.
229 */
230 final int targetStep = 100;
231
232 /*
233 * If we are close to the beginning of the trace, make sure we only look
234 * for the events before the startRank.
235 */
236 int step = (startRank < targetStep ? (int) startRank : targetStep);
237
238 long currentRank = startRank;
239 try {
240 while (currentRank > 0) {
241 currentRank = Math.max(currentRank - step, 0);
242
243 EventMatchingRequest req = new EventMatchingRequest(currentRank, step, predicate, true);
244 trace.sendRequest(req);
245 req.waitForCompletion();
246
247 ITmfEvent event = req.getFoundEvent();
248 if (event != null) {
249 /* We found an actual event, return it! */
250 return event;
251 }
252 /* Keep searching, next loop */
253
254 }
255 } catch (InterruptedException e) {
256 return null;
257 }
258
259 /*
260 * We searched up to the beginning of the trace and didn't find
261 * anything.
262 */
263 return null;
264
265 }
266
267 /**
268 * Event request looking for an event matching a Predicate.
269 */
270 private static class EventMatchingRequest extends TmfEventRequest {
271
272 private final Predicate<ITmfEvent> fPredicate;
273 private final boolean fReturnLast;
274
275 private @Nullable ITmfEvent fFoundEvent = null;
276
277 /**
278 * Basic constructor, will query the trace until the end.
279 *
280 * @param startRank
281 * The rank at which to start, use 0 for the beginning
282 * @param predicate
283 * The predicate to test against each event
284 * @param returnLast
285 * Should we return the last or first event found. If false,
286 * the request ends as soon as a matching event is found. If
287 * false, we will go through all events to find a possible
288 * last-match.
289 */
290 public EventMatchingRequest(long startRank, Predicate<ITmfEvent> predicate, boolean returnLast) {
291 super(ITmfEvent.class, startRank, ALL_DATA, ExecutionType.FOREGROUND);
292 fPredicate = predicate;
293 fReturnLast = returnLast;
294 }
295
296 /**
297 * Basic constructor, will query the trace the limit is reached.
298 *
299 * @param startRank
300 * The rank at which to start, use 0 for the beginning
301 * @param limit
302 * The limit on the number of events
303 * @param predicate
304 * The predicate to test against each event
305 * @param returnLast
306 * Should we return the last or first event found. If false,
307 * the request ends as soon as a matching event is found. If
308 * false, we will go through all events to find a possible
309 * last-match.
310 */
311 public EventMatchingRequest(long startRank, int limit, Predicate<ITmfEvent> predicate, boolean returnLast) {
312 super(ITmfEvent.class, startRank, limit, ExecutionType.FOREGROUND);
313 fPredicate = predicate;
314 fReturnLast = returnLast;
315 }
316
317 public @Nullable ITmfEvent getFoundEvent() {
318 return fFoundEvent;
319 }
320
321 @Override
322 public void handleData(ITmfEvent event) {
323 super.handleData(event);
324
325 if (fPredicate.test(event)) {
326 fFoundEvent = event;
327 if (!fReturnLast) {
328 this.done();
329 }
330 }
331 }
332 }
b8585c7c 333}
This page took 0.0643 seconds and 5 git commands to generate.