| /******************************************************************************* |
| * Copyright (c) 2015 Ericsson |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * France Lapointe Nguyen - Initial API and implementation |
| * Bernd Hufmann - MOve abstract class to TMF |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.table; |
| |
| import java.util.Comparator; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.function.Predicate; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.tracecompass.common.core.NonNullUtils; |
| import org.eclipse.tracecompass.segmentstore.core.ISegment; |
| import org.eclipse.tracecompass.segmentstore.core.ISegmentStore; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; |
| import org.eclipse.tracecompass.tmf.ui.viewers.table.ISortingLazyContentProvider; |
| |
| import com.google.common.collect.Iterables; |
| |
| /** |
| * Content provider for the latency table viewers. |
| * |
| * @author France Lapointe Nguyen |
| */ |
| public class SegmentStoreContentProvider implements ISortingLazyContentProvider { |
| |
| /** |
| * Class that wraps a segment store and a time range. |
| * |
| * Note: this class is not thread-safe. It is not meant to be used by many |
| * threads simultaneously. Many methods are synchronized, so at best, the |
| * performances will be bad, but at worst, it may return false results. |
| * |
| * @author Geneviève Bastien |
| * @param <E> |
| * The type of segment in the segment store |
| */ |
| public static class SegmentStoreWithRange<E extends ISegment> implements Iterable<E> { |
| /** |
| * Constant used in the {@link #getElement(long)} method to return the |
| * last element of the store |
| */ |
| public static final long LAST = Long.MIN_VALUE; |
| private final ISegmentStore<E> fSegmentStore; |
| private final TmfTimeRange fRange; |
| |
| private @Nullable Comparator<ISegment> fComparator = null; |
| private @Nullable Iterable<E> fIterable = null; |
| private @Nullable Predicate<E> fPredicate = null; |
| |
| private long fLastReadPos = -1; |
| private @Nullable Iterator<E> fIterator = null; |
| |
| /** |
| * Constructor |
| * |
| * @param segStore |
| * The segment store |
| * @param range |
| * The time range to get for this segment store |
| */ |
| public SegmentStoreWithRange(ISegmentStore<E> segStore, TmfTimeRange range) { |
| fSegmentStore = segStore; |
| fRange = range; |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param segStore |
| * The segment store |
| * @param range |
| * The time range to get for this segment store |
| * @param predicate |
| * An extra predicate to further filter the segments |
| */ |
| public SegmentStoreWithRange(ISegmentStore<E> segStore, TmfTimeRange range, Predicate<E> predicate) { |
| fSegmentStore = segStore; |
| fRange = range; |
| fPredicate = predicate; |
| } |
| |
| @Override |
| public Iterator<E> iterator() { |
| return NonNullUtils.checkNotNull(getIterable().iterator()); |
| } |
| |
| /** |
| * Set the comparator for this store |
| * |
| * @param comparator |
| * The comparator to use for this store |
| */ |
| public void setComparator(Comparator<ISegment> comparator) { |
| fComparator = comparator; |
| fIterable = null; |
| resetIterator(); |
| } |
| |
| /** |
| * Get the iterable to iterate over this segment store |
| * |
| * @return The iterable object to iterate through the segment store |
| */ |
| private Iterable<E> getIterable() { |
| Iterable<E> iterable = fIterable; |
| if (iterable == null) { |
| Comparator<ISegment> comparator = fComparator; |
| Predicate<? super E> predicate = fPredicate; |
| if (comparator != null) { |
| iterable = fSegmentStore.getIntersectingElements(fRange.getStartTime().toNanos(), fRange.getEndTime().toNanos(), comparator); |
| } else { |
| iterable = fSegmentStore.getIntersectingElements(fRange.getStartTime().toNanos(), fRange.getEndTime().toNanos()); |
| } |
| if (predicate != null) { |
| iterable = Iterables.filter(iterable, input -> predicate.test(input)); |
| } |
| fIterable = iterable; |
| } |
| return iterable; |
| } |
| |
| private Iterator<? extends @NonNull ISegment> resetIterator() { |
| Iterator<E> iterator = NonNullUtils.checkNotNull(getIterable().iterator()); |
| fIterator = iterator; |
| fLastReadPos = -1; |
| return iterator; |
| } |
| |
| /** |
| * Get the element at position index. If the index is {@link #LAST}, it |
| * will return the last element from the end of the iterator, if a |
| * comparator was specified, otherwise it will return the first event. |
| * This method with {@link #LAST} is meant to be used with sorted |
| * stores. |
| * |
| * @param index |
| * The index of the requested element. If index is |
| * {@link #LAST} it will return the last element, at the end |
| * of the iterator. |
| * @return The segment at position index or <code>null</code> if it is |
| * not available |
| */ |
| public @Nullable ISegment getElement(long index) { |
| long idx = index; |
| // Special code path if we are looking for an element from the end there is a comparator |
| if (index == LAST) { |
| Comparator<@NonNull ISegment> comparator = fComparator; |
| if (comparator != null) { |
| return getLastElement(comparator); |
| } |
| // No comparator, so impossible the easily get last element, |
| // just return the first as it is random anyway |
| idx = 0; |
| } |
| Iterable<? extends @NonNull ISegment> iterable = fIterable; |
| if (iterable instanceof List<?> && idx <= Integer.MAX_VALUE) { |
| return Iterables.get(iterable, (int) idx, null); |
| } |
| Iterator<? extends @NonNull ISegment> iterator = fIterator; |
| if (iterator == null || idx <= fLastReadPos) { |
| iterator = resetIterator(); |
| } |
| ISegment segment = null; |
| while (fLastReadPos < idx && iterator.hasNext()) { |
| fLastReadPos++; |
| segment = NonNullUtils.checkNotNull(iterator.next()); |
| } |
| if (fLastReadPos == idx) { |
| return segment; |
| } |
| return null; |
| } |
| |
| private @Nullable ISegment getLastElement(Comparator<@NonNull ISegment> comparator) { |
| Iterable<? extends ISegment> baseIterable = fIterable; |
| if (baseIterable instanceof List<?>) { |
| return Iterables.getLast(baseIterable, null); |
| } |
| // Not a trivial get, so get an iterable for the reverse comparator |
| // and fetch first element |
| Predicate<? super E> predicate = fPredicate; |
| Iterable<E> iterable = fSegmentStore.getIntersectingElements(fRange.getStartTime().toNanos(), fRange.getEndTime().toNanos(), comparator.reversed()); |
| if (predicate != null) { |
| iterable = Iterables.filter(iterable, input -> predicate.test(input)); |
| } |
| // FIXME: The cast turns an error into a warning for this null |
| // value, but it is completely unnecessary otherwise |
| return Iterables.getFirst((Iterable<? extends ISegment>) iterable, null); |
| } |
| |
| /** |
| * Get the number of segments |
| * |
| * TODO: Try to live without this method, this is not lazy enough |
| * |
| * @return The number of segment in the current iterable |
| */ |
| public long getSegmentCount() { |
| return Iterables.size(getIterable()); |
| } |
| } |
| |
| /** |
| * Table viewer of the latency table viewer |
| */ |
| private @Nullable TableViewer fTableViewer = null; |
| |
| /** |
| * Segment comparator |
| */ |
| private @Nullable Comparator<ISegment> fComparator = null; |
| private @Nullable SegmentStoreWithRange<?> fStore; |
| |
| @Override |
| public void updateElement(int index) { |
| final TableViewer tableViewer = fTableViewer; |
| SegmentStoreWithRange<?> store = fStore; |
| if (tableViewer == null || store == null) { |
| return; |
| } |
| tableViewer.replace(store.getElement(index), index); |
| } |
| |
| @Override |
| public void dispose() { |
| fStore = null; |
| fTableViewer = null; |
| fComparator = null; |
| } |
| |
| @Override |
| public void inputChanged(@Nullable Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) { |
| fTableViewer = (TableViewer) viewer; |
| if (newInput instanceof SegmentStoreWithRange) { |
| SegmentStoreWithRange<?> sswr = (SegmentStoreWithRange<?>) newInput; |
| Comparator<ISegment> comparator = fComparator; |
| if (comparator != null) { |
| sswr.setComparator(comparator); |
| } |
| fStore = sswr; |
| } else if (newInput instanceof ISegmentStore<?>) { |
| ISegmentStore<? extends ISegment> segmentStore = (ISegmentStore<?>) newInput; |
| SegmentStoreWithRange<?> sswr = new SegmentStoreWithRange<>(segmentStore, TmfTimeRange.ETERNITY); |
| Comparator<ISegment> comparator = fComparator; |
| |
| if (comparator != null) { |
| sswr.setComparator(comparator); |
| } |
| fStore = sswr; |
| } else { |
| fStore = null; |
| } |
| } |
| |
| @Override |
| public void setSortOrder(@Nullable Comparator<?> comparator) { |
| SegmentStoreWithRange<?> store = fStore; |
| final TableViewer tableViewer = fTableViewer; |
| if (comparator == null || store == null || tableViewer == null) { |
| return; |
| } |
| @SuppressWarnings("unchecked") |
| Comparator<ISegment> comp = (Comparator<ISegment>) comparator; |
| fComparator = comp; |
| store.setComparator(comp); |
| tableViewer.refresh(); |
| } |
| |
| /** |
| * Get the segment count |
| * |
| * @return the segment count |
| */ |
| public long getSegmentCount() { |
| SegmentStoreWithRange<?> store = fStore; |
| return (store == null ? 0 : store.getSegmentCount()); |
| } |
| } |