org.eclipse.tracecompass.tmf.ui.markers.PeriodicMarkerEventSource.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.tracecompass.tmf.ui.markers.PeriodicMarkerEventSource.java

Source

/*******************************************************************************
 * Copyright (c) 2016 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:
 *   Patrick Tasse - Initial API and implementation
 *******************************************************************************/

package org.eclipse.tracecompass.tmf.ui.markers;

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.math.Fraction;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.swt.graphics.RGBA;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEvent;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.IMarkerEventSource;
import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.MarkerEvent;

/**
 * Marker event source that produces periodic markers.
 *
 * @since 2.0
 */
@NonNullByDefault
public class PeriodicMarkerEventSource implements IMarkerEventSource {

    /**
     * Reference marker time and index
     */
    public static class Reference {

        /** Reference marker index 0 at time 0 */
        public static final Reference ZERO = new Reference(0L, 0);

        private final long time;
        private final long index;

        /**
         * Constructor
         *
         * @param time
         *            the reference marker time in time units
         * @param index
         *            the reference marker index
         */
        public Reference(long time, int index) {
            this.time = time;
            this.index = index;
        }

        /**
         * Constructor
         *
         * @param time
         *            the reference marker time in time units
         * @param index
         *            the reference marker index
         * @since 2.2
         */
        public Reference(long time, long index) {
            this.time = time;
            this.index = index;
        }

        @Override
        public String toString() {
            return String.format("[%d, %d]", time, index); //$NON-NLS-1$
        }
    }

    private final String fCategory;
    private final Reference fReference;
    private final double fPeriod;
    private final long fPeriodInteger;
    private @Nullable Fraction fPeriodFraction;
    private final long fRollover;
    private final RGBA fColor;
    private final @Nullable RGBA fOddColor;
    private final boolean fForeground;

    /**
     * Constructs a periodic marker event source with line markers at period
     * boundaries.
     * <p>
     * The markers will have the given category and color. The reference defines
     * the marker with the given index to be at the specified time.
     *
     * @param category
     *            the marker category
     * @param reference
     *            the reference marker time and index
     * @param period
     *            the period in time units
     * @param rollover
     *            the number of periods before the index rolls-over to 0, or 0
     *            for no roll-over
     * @param color
     *            the marker color
     * @param foreground
     *            true if the marker is drawn in foreground, and false otherwise
     */
    public PeriodicMarkerEventSource(String category, Reference reference, double period, long rollover, RGBA color,
            boolean foreground) {
        this(category, reference, period, rollover, foreground, color, null);
    }

    /**
     * Constructs a periodic marker event source with alternating shading
     * markers.
     * <p>
     * The markers will have the given category. Periods with even index will be
     * shaded with the even color. Periods with odd index will be shaded with
     * the odd color. The reference defines the marker with the given index to
     * be at the specified time.
     *
     * @param category
     *            the marker category
     * @param reference
     *            the reference marker time and index
     * @param period
     *            the period in time units
     * @param rollover
     *            the number of periods before the index rolls-over to 0, or 0
     *            for no roll-over
     * @param evenColor
     *            the even marker color
     * @param oddColor
     *            the odd marker color
     * @param foreground
     *            true if the marker is drawn in foreground, and false otherwise
     */
    public PeriodicMarkerEventSource(String category, Reference reference, double period, long rollover,
            RGBA evenColor, RGBA oddColor, boolean foreground) {
        this(category, reference, period, rollover, foreground, evenColor, oddColor);
    }

    /* Private constructor. The order of parameters is changed to make it unique. */
    private PeriodicMarkerEventSource(String category, Reference reference, double period, long rollover,
            boolean foreground, RGBA evenColor, @Nullable RGBA oddColor) {
        if (period <= 0) {
            throw new IllegalArgumentException("period cannot be less than or equal to zero"); //$NON-NLS-1$
        }
        if (rollover < 0) {
            throw new IllegalArgumentException("rollover cannot be less than zero"); //$NON-NLS-1$
        }
        fCategory = category;
        fReference = reference;
        fPeriod = period;
        fPeriodInteger = (long) period;
        try {
            fPeriodFraction = Fraction.getFraction(fPeriod - fPeriodInteger);
        } catch (ArithmeticException e) {
            /* can't convert to fraction, use floating-point arithmetic */
            fPeriodFraction = null;
        }
        fRollover = rollover;
        fColor = evenColor;
        fOddColor = oddColor;
        fForeground = foreground;
    }

    @Override
    public List<String> getMarkerCategories() {
        return Arrays.asList(fCategory);
    }

    @Override
    public List<IMarkerEvent> getMarkerList(String category, long startTime, long endTime, long resolution,
            IProgressMonitor monitor) {
        if (startTime > endTime) {
            return Collections.emptyList();
        }
        List<IMarkerEvent> markers = new ArrayList<>();
        /* Subtract 1.5 periods to ensure previous marker is included */
        long time = startTime - Math.max(Math.round(1.5 * fPeriod), resolution);
        Reference reference = adjustReference(fReference, time);
        IMarkerEvent markerEvent = null;
        while (true) {
            long index = Math.round((time - reference.time) / fPeriod) + reference.index;
            time = Math.round((index - reference.index) * fPeriod) + reference.time;
            long duration = (fOddColor == null) ? 0
                    : Math.round((index + 1 - reference.index) * fPeriod) + reference.time - time;
            long labelIndex = index;
            if (fRollover != 0) {
                labelIndex %= fRollover;
                if (labelIndex < 0) {
                    labelIndex += fRollover;
                }
            }
            /* Add previous marker if current is visible */
            if ((time >= startTime || time + duration > startTime) && markerEvent != null) {
                markers.add(markerEvent);
            }
            RGBA color = (fOddColor == null) ? fColor : (index % 2 == 0) ? fColor : fOddColor;
            markerEvent = new MarkerEvent(null, time, duration, fCategory, color, getMarkerLabel(labelIndex),
                    fForeground);
            if (time > endTime) {
                /* The next marker out of range is included */
                markers.add(markerEvent);
                break;
            }
            time += Math.max(Math.round(fPeriod), resolution);
        }
        return markers;
    }

    /*
     * Adjust to a reference that is closer to the start time, to avoid rounding
     * errors in floating point calculations with large numbers.
     */
    private Reference adjustReference(Reference baseReference, long time) {
        long offsetIndex = (long) ((time - baseReference.time) / fPeriod);
        long offsetTime = 0;
        Fraction fraction = fPeriodFraction;
        if (fraction != null) {
            /*
             * If period = int num/den, find an offset index that is an exact
             * multiple of den and calculate index * period = (index * int) +
             * (index / den * num), all exact calculations.
             */
            offsetIndex = offsetIndex - offsetIndex % fraction.getDenominator();
            offsetTime = offsetIndex * fPeriodInteger
                    + offsetIndex / fraction.getDenominator() * fraction.getNumerator();
        } else {
            /*
             * Couldn't compute fractional part as fraction, use simple
             * multiplication but with possible rounding error.
             */
            offsetTime = Math.round(offsetIndex * fPeriod);
        }
        Reference reference = new Reference(baseReference.time + offsetTime, baseReference.index + offsetIndex);
        return reference;
    }

    /**
     * Get the marker label for the given marker index.
     * <p>
     * This method can be overridden by clients.
     *
     * @param index
     *            the marker index
     * @return the marker label
     */
    public String getMarkerLabel(long index) {
        return checkNotNull(Long.toString(index));
    }
}