org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Ericsson
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Alexandre Montplaisir - Initial API and implementation
 *******************************************************************************/

package org.eclipse.tracecompass.tmf.core.trace;

import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;

import org.eclipse.core.runtime.IProgressMonitor;
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;
import com.google.common.collect.Lists;

/**
 * Utility methods for ITmfTrace's.
 *
 * @author Alexandre Montplaisir
 */
@NonNullByDefault
public final class TmfTraceUtils {

    private static final int MAX_NB_BINARY_BYTES = 2048;

    private TmfTraceUtils() {
    }

    /**
     * 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
     * @param moduleClass
     *            Returned modules must extend this class
     * @param id
     *            The ID of the analysis module
     * @return The analysis module with specified class and ID, or null if no
     *         such module exists.
     */
    public static @Nullable <T extends IAnalysisModule> T getAnalysisModuleOfClass(ITmfTrace trace,
            Class<T> moduleClass, String id) {
        Iterable<T> modules = getAnalysisModulesOfClass(trace, moduleClass);
        for (T module : modules) {
            if (id.equals(module.getId())) {
                return module;
            }
        }
        return null;
    }

    /**
     * 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 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();
        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;
    }

    /**
     * Return the first result of the first aspect that resolves as non null for
     * the event received in parameter. If the returned value is not null, it
     * can be safely cast to the aspect's class proper return type.
     *
     * @param trace
     *            The trace for which you want the event aspects
     * @param aspectClass
     *            The class of the aspect(s) to resolve
     * @param event
     *            The event for which to get the aspect
     * @return The first result of the
     *         {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
     *         for the event or {@code null} otherwise
     */
    public static <T extends ITmfEventAspect<?>> @Nullable Object resolveEventAspectOfClassForEvent(ITmfTrace trace,
            Class<T> aspectClass, ITmfEvent event) {
        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 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
     * @param aspectClass
     *            The class of the aspect(s) to resolve
     * @param event
     *            The event for which to get the aspect
     * @return Integer of the first result of the
     *         {@link ITmfEventAspect#resolve(ITmfEvent)} that returns non null
     *         for the event or {@code null} otherwise
     * @since 2.0
     */
    public static <T extends ITmfEventAspect<Integer>> @Nullable Integer resolveIntEventAspectOfClassForEvent(
            ITmfTrace trace, Class<T> aspectClass, ITmfEvent event) {
        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);
    }

    /**
     * Checks for text file.
     *
     * Note that it checks for binary value 0 in the first MAX_NB_BINARY_BYTES
     * bytes to determine if the file is text.
     *
     * @param file
     *            the file to check. Caller has to make sure that file exists.
     * @return true if it is binary else false
     * @throws IOException
     *             if IOException occurs
     * @since 1.2
     */
    public static boolean isText(File file) throws IOException {
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) {
            int count = 0;
            int val = bufferedInputStream.read();
            while ((count < MAX_NB_BINARY_BYTES) && (val >= 0)) {
                if (val == 0) {
                    return false;
                }
                count++;
                val = bufferedInputStream.read();
            }
        }
        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
     * @param monitor
     *            Optional progress monitor that can be used to cancel the
     *            operation
     * @return The first event matching the predicate, or null if the end of the
     *         trace was reached and no event was found
     * @since 2.1
     */
    public static @Nullable ITmfEvent getNextEventMatching(ITmfTrace trace, long startRank,
            Predicate<ITmfEvent> predicate, @Nullable IProgressMonitor monitor) {
        if (monitor != null && monitor.isCanceled()) {
            return null;
        }

        /* 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 {
            /* Check periodically if the job was cancelled */
            req.waitForStart();
            while (req.isRunning()) {
                Thread.sleep(200);
                if (monitor != null && monitor.isCanceled()) {
                    req.cancel();
                    return null;
                }
            }
            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
     * @param monitor Optional progress monitor that can be used to cancel the
     *            operation
     * @return The first event found matching the predicate, or null if the
     *         beginning of the trace was reached and no event was found
     * @since 2.1
     */
    public static @Nullable ITmfEvent getPreviousEventMatching(ITmfTrace trace, long startRank,
            Predicate<ITmfEvent> predicate, @Nullable IProgressMonitor monitor) {
        if (monitor != null && monitor.isCanceled()) {
            return null;
        }
        /*
         * We are going to do a series of queries matching the trace's cache
         * size in length (which should minimize on-disk seeks), then iterate on
         * the found events in reverse order until we find a match.
         */
        int step = trace.getCacheSize();

        /*
         * If we are close to the beginning of the trace, make sure we only look
         * for the events before the startRank.
         */
        if (startRank < step) {
            step = (int) startRank;
        }

        long currentRank = startRank;
        try {
            while (currentRank > 0) {
                currentRank = Math.max(currentRank - step, 0);

                List<ITmfEvent> list = new ArrayList<>(step);
                ArrayFillingRequest req = new ArrayFillingRequest(currentRank, step, list);
                trace.sendRequest(req);

                /* Check periodically if the job was cancelled */
                req.waitForStart();
                while (req.isRunning()) {
                    Thread.sleep(200);
                    if (monitor != null && monitor.isCanceled()) {
                        req.cancel();
                        return null;
                    }
                }
                req.waitForCompletion();

                Optional<ITmfEvent> matchingEvent = Lists.reverse(list).stream().filter(predicate).findFirst();

                if (matchingEvent.isPresent()) {
                    /* We found an event matching, return it! */
                    return matchingEvent.get();
                }
                /* 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();
                }
            }
        }
    }

    /**
     * Event request that simply puts all returned events into a list passed in
     * parameter.
     */
    private static class ArrayFillingRequest extends TmfEventRequest {

        private final List<ITmfEvent> fList;

        public ArrayFillingRequest(long startRank, int limit, List<ITmfEvent> listToFill) {
            super(ITmfEvent.class, startRank, limit, ExecutionType.FOREGROUND);
            fList = listToFill;
        }

        @Override
        public void handleData(ITmfEvent event) {
            super.handleData(event);
            fList.add(event);
        }

    }
}