timing: Have the segment store view use lazy iterables
authorGeneviève Bastien <gbastien+lttng@versatic.net>
Fri, 21 Oct 2016 19:35:10 +0000 (15:35 -0400)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Thu, 11 May 2017 22:24:44 +0000 (18:24 -0400)
Views will make lazy-load the data using an iterator instead of a
collection of segments. This will avoid having to have all segments in
memory.

Change-Id: I9cb9f2f9db621cb9789537882ab95b8ba239e59a
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/83724
Reviewed-by: Hudson CI
analysis/org.eclipse.tracecompass.analysis.timing.ui.swtbot.tests/META-INF/MANIFEST.MF
analysis/org.eclipse.tracecompass.analysis.timing.ui.swtbot.tests/src/org/eclipse/tracecompass/analysis/timing/ui/swtbot/tests/callgraph/CallGraphDensityViewTest.java
analysis/org.eclipse.tracecompass.analysis.timing.ui.swtbot.tests/src/org/eclipse/tracecompass/analysis/timing/ui/swtbot/tests/table/SegmentTableTest.java
analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/analysis/timing/ui/views/segmentstore/density/AbstractSegmentStoreDensityView.java
analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/analysis/timing/ui/views/segmentstore/density/AbstractSegmentStoreDensityViewer.java
analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/analysis/timing/ui/views/segmentstore/density/ISegmentStoreDensityViewerDataListener.java
analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/analysis/timing/ui/views/segmentstore/table/AbstractSegmentStoreTableViewer.java
analysis/org.eclipse.tracecompass.analysis.timing.ui/src/org/eclipse/tracecompass/internal/analysis/timing/ui/views/segmentstore/table/SegmentStoreContentProvider.java
common/org.eclipse.tracecompass.common.core/annotations/com/google/common/collect/Iterables.eea [new file with mode: 0644]
common/org.eclipse.tracecompass.common.core/annotations/java/util/Comparator.eea [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.segmentstore.core/src/org/eclipse/tracecompass/segmentstore/core/ISegmentStore.java

index 92135678b91ea943ee2a6af73f0a5daec1fa1c2e..7e30d19744b282abc63317fad7df53a84e4b4808 100644 (file)
@@ -30,6 +30,7 @@ Require-Bundle: org.apache.log4j,
  org.junit,
  org.eclipse.tracecompass.analysis.timing.core.tests,
  org.eclipse.tracecompass.tmf.ui.tests,
- org.eclipse.tracecompass.segmentstore.core
+ org.eclipse.tracecompass.segmentstore.core,
+ org.eclipse.tracecompass.datastore.core
 Import-Package: com.google.common.collect,
  org.swtchart
index a7e0d35e5ad5d2e012cb21cff38d199f64261339..0f58ebb5cc4a2d314311c1d2f66bee48b10840ed 100644 (file)
@@ -66,16 +66,28 @@ public class CallGraphDensityViewTest extends AggregationTreeTest {
     private static final String CALLGRAPHDENSITY_ID = CallGraphDensityView.ID;
 
     private final @NonNull ISegmentStoreDensityViewerDataListener fSyncListener = new ISegmentStoreDensityViewerDataListener() {
+        @Deprecated
         @Override
         public void dataChanged(List<ISegment> newData) {
             fLatch.countDown();
         }
 
+        @Deprecated
         @Override
         public void dataSelectionChanged(@Nullable List<@NonNull ISegment> newSelectionData) {
             // do nothing
         }
 
+        @Override
+        public void viewDataChanged(@NonNull Iterable<? extends @NonNull ISegment> newData) {
+            fLatch.countDown();
+        }
+
+        @Override
+        public void selectedDataChanged(@Nullable Iterable<? extends @NonNull ISegment> newSelectionData) {
+            // do nothing
+        }
+
     };
     private SWTWorkbenchBot fBot;
     private SWTBotView fView;
index 18b1b0eb188d28683b9d36bf2131f3f775bdb501..235416737d0ca7bc9b4067e7ba92df78684531fa 100644 (file)
@@ -20,7 +20,8 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -45,6 +46,7 @@ import org.eclipse.tracecompass.analysis.timing.core.segmentstore.IAnalysisProgr
 import org.eclipse.tracecompass.analysis.timing.core.segmentstore.ISegmentStoreProvider;
 import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.table.AbstractSegmentStoreTableView;
 import org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.table.AbstractSegmentStoreTableViewer;
+import org.eclipse.tracecompass.internal.provisional.segmentstore.core.BasicSegment2;
 import org.eclipse.tracecompass.segmentstore.core.BasicSegment;
 import org.eclipse.tracecompass.segmentstore.core.ISegment;
 import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
@@ -254,7 +256,7 @@ public class SegmentTableTest {
      */
     @Test
     public void climbTest() {
-        List<@NonNull ISegment> fixture = new ArrayList<>();
+        ISegmentStore<@NonNull ISegment> fixture = SegmentStoreFactory.createSegmentStore();
         for (int i = 0; i < 100; i++) {
             fixture.add(createSegment(i, 2 * i));
         }
@@ -278,14 +280,14 @@ public class SegmentTableTest {
      */
     @Test
     public void decrementingTest() {
-        List<@NonNull ISegment> fixture = new ArrayList<>();
+        ISegmentStore<@NonNull ISegment> fixture = SegmentStoreFactory.createSegmentStore();
         for (int i = 100; i >= 0; i--) {
             fixture.add(createSegment(i, 2 * i));
         }
         assertNotNull(getTable());
         getTable().updateModel(fixture);
         SWTBotTable tableBot = new SWTBotTable(getTable().getTableViewer().getTable());
-        fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "100", 0, 2));
+        fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "0", 0, 2));
         tableBot.header("Duration").click();
         fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "0", 0, 2));
         tableBot.header("Duration").click();
@@ -299,14 +301,14 @@ public class SegmentTableTest {
      */
     @Test
     public void smallTest() {
-        List<@NonNull ISegment> fixture = new ArrayList<>();
+        ISegmentStore<@NonNull ISegment> fixture = SegmentStoreFactory.createSegmentStore();
         for (int i = 1; i >= 0; i--) {
             fixture.add(createSegment(i, 2 * i));
         }
         assertNotNull(getTable());
         getTable().updateModel(fixture);
         SWTBotTable tableBot = new SWTBotTable(getTable().getTableViewer().getTable());
-        fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "1", 0, 2));
+        fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "0", 0, 2));
         tableBot.header("Duration").click();
         fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "0", 0, 2));
         tableBot.header("Duration").click();
@@ -321,9 +323,9 @@ public class SegmentTableTest {
     @Test
     public void largeTest() {
         final int size = 1000000;
-        ISegment[] fixture = new ISegment[size];
+        ISegmentStore<@NonNull ISegment> fixture = SegmentStoreFactory.createSegmentStore();
         for (int i = 0; i < size; i++) {
-            fixture[i] = createSegment(i, 2 * i);
+            fixture.add(createSegment(i, 2 * i));
         }
         assertNotNull(getTable());
         getTable().updateModel(fixture);
@@ -347,16 +349,16 @@ public class SegmentTableTest {
         Random rnd = new Random();
         rnd.setSeed(1234);
         final int size = 1000000;
-        ISegment[] fixture = new ISegment[size];
+        ISegmentStore<@NonNull ISegment> fixture = SegmentStoreFactory.createSegmentStore();
         for (int i = 0; i < size; i++) {
             int start = Math.abs(rnd.nextInt(100000000));
             int end = start + Math.abs(rnd.nextInt(1000000));
-            fixture[i] = (createSegment(start, end));
+            fixture.add(createSegment(start, end));
         }
         assertNotNull(getTable());
         getTable().updateModel(fixture);
         SWTBotTable tableBot = new SWTBotTable(getTable().getTableViewer().getTable());
-        fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "894,633", 0, 2));
+        fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "374,153", 0, 2));
         tableBot.header("Duration").click();
         fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "0", 0, 2));
         tableBot.header("Duration").click();
@@ -374,7 +376,7 @@ public class SegmentTableTest {
     public void gaussianNoiseTest() {
         Random rnd = new Random();
         rnd.setSeed(1234);
-        List<@NonNull ISegment> fixture = new ArrayList<>();
+        ISegmentStore<@NonNull ISegment> fixture = SegmentStoreFactory.createSegmentStore();
         for (int i = 1; i <= 1000000; i++) {
             int start = Math.abs(rnd.nextInt(100000000));
             final int delta = Math.abs(rnd.nextInt(1000));
@@ -384,13 +386,41 @@ public class SegmentTableTest {
         assertNotNull(getTable());
         getTable().updateModel(fixture);
         SWTBotTable tableBot = new SWTBotTable(getTable().getTableViewer().getTable());
-        fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "400,689", 0, 2));
+        fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "23,409", 0, 2));
         tableBot.header("Duration").click();
         fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "0", 0, 2));
         tableBot.header("Duration").click();
         fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "998,001", 0, 2));
     }
 
+    /**
+     * Test table with an on-disk segment store that is lazy loaded in the table
+     *
+     * @throws IOException
+     */
+    @Test
+    public void onDiskSegStoreTest() throws IOException {
+        Path segmentFile = Files.createTempFile("tmpSegStore", ".tmp");
+        try {
+            final int size = 1000000;
+            ISegmentStore<@NonNull BasicSegment2> fixture = SegmentStoreFactory.createOnDiskSegmentStore(segmentFile, BasicSegment2.BASIC_SEGMENT_READ_FACTORY);
+            for (int i = 0; i < size; i++) {
+                fixture.add(new BasicSegment2(i, 2 * i));
+            }
+            assertNotNull(getTable());
+            getTable().updateModel(fixture);
+            SWTBotTable tableBot = new SWTBotTable(getTable().getTableViewer().getTable());
+            fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "0", 0, 2));
+            tableBot.header("Duration").click();
+            fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "0", 0, 2));
+            tableBot.header("Duration").click();
+            // FIXME: Should be 999,999, but sorting on disk does not work well yet
+            fBot.waitUntil(ConditionHelpers.isTableCellFilled(tableBot, "818,799", 0, 2));
+        } finally {
+            Files.delete(segmentFile);
+        }
+    }
+
     /**
      * Test creating a tsv
      *
@@ -402,7 +432,7 @@ public class SegmentTableTest {
     @Test
     public void testWriteToTsv() throws NoSuchMethodException, IOException {
 
-        List<@NonNull ISegment> fixture = new ArrayList<>();
+        ISegmentStore<@NonNull ISegment> fixture = SegmentStoreFactory.createSegmentStore();
         for (int i = 1; i <= 20; i++) {
             int start = i;
             final int delta = i;
index dfb4df0e85d93f96cfe62b8d188d67ea31b1ae77..c6056bbd1b9362f918ead0b967ae7b568ed6545c 100644 (file)
@@ -11,6 +11,7 @@ package org.eclipse.tracecompass.analysis.timing.ui.views.segmentstore.density;
 
 import java.util.List;
 
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IToolBarManager;
@@ -54,6 +55,7 @@ public abstract class AbstractSegmentStoreDensityView extends TmfView {
      * Used to keep the table in sync with the density viewer.
      */
     private final class DataChangedListener implements ISegmentStoreDensityViewerDataListener {
+        @Deprecated
         @Override
         public void dataChanged(List<ISegment> data) {
             updateTableModel(data);
@@ -62,14 +64,33 @@ public abstract class AbstractSegmentStoreDensityView extends TmfView {
         private void updateTableModel(@Nullable List<ISegment> data) {
             final AbstractSegmentStoreTableViewer viewer = fTableViewer;
             if (viewer != null && data != null) {
-                viewer.updateModel(data.toArray(new ISegment[] {}));
+                viewer.updateModel(data);
             }
         }
 
+        private void updateTableModel(@Nullable Iterable<? extends ISegment> data) {
+            final AbstractSegmentStoreTableViewer viewer = fTableViewer;
+            if (viewer != null && data != null) {
+                viewer.updateModel(data);
+            }
+        }
+
+        @Deprecated
         @Override
         public void dataSelectionChanged(@Nullable List<ISegment> data) {
             updateTableModel(data);
         }
+
+        @Override
+        public void viewDataChanged(@NonNull Iterable<? extends @NonNull ISegment> newData) {
+            updateTableModel(newData);
+        }
+
+        @Override
+        public void selectedDataChanged(@Nullable Iterable<? extends @NonNull ISegment> newSelectionData) {
+            updateTableModel(newSelectionData);
+        }
+
     }
 
     @Override
index 559cb2109ea3fcd5fbeee5508cbd1bf4987df83f..5d05b0d52fc25f3d1aa4b5dd4015fabb13c7633f 100644 (file)
@@ -14,11 +14,11 @@ import static org.eclipse.tracecompass.common.core.NonNullUtils.nullToEmptyStrin
 import java.text.Format;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import java.util.function.Predicate;
 
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Color;
@@ -32,6 +32,7 @@ import org.eclipse.tracecompass.common.core.NonNullUtils;
 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.MouseDragZoomProvider;
 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.MouseSelectionProvider;
 import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.density.SimpleTooltipProvider;
+import org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.table.SegmentStoreContentProvider.SegmentStoreWithRange;
 import org.eclipse.tracecompass.segmentstore.core.ISegment;
 import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
 import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
@@ -56,9 +57,6 @@ import org.swtchart.LineStyle;
 import org.swtchart.Range;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
 
 /**
  * Displays the segment store provider data in a density chart.
@@ -126,10 +124,7 @@ public abstract class AbstractSegmentStoreDensityViewer extends TmfViewer {
         return TmfTraceManager.getInstance().getActiveTrace();
     }
 
-    private void updateDisplay(List<ISegment> data) {
-        if (data.isEmpty()) {
-            return;
-        }
+    private void updateDisplay(SegmentStoreWithRange<ISegment> data) {
         IBarSeries series = (IBarSeries) fChart.getSeriesSet().createSeries(SeriesType.BAR, Messages.AbstractSegmentStoreDensityViewer_SeriesLabel);
         series.setVisible(true);
         series.setBarPadding(0);
@@ -140,7 +135,12 @@ public abstract class AbstractSegmentStoreDensityViewer extends TmfViewer {
         double[] xOrigSeries = new double[width];
         double[] yOrigSeries = new double[width];
         Arrays.fill(yOrigSeries, 1.0);
-        long maxLength = data.get(data.size() - 1).getLength();
+        data.setComparator(SegmentComparators.INTERVAL_LENGTH_COMPARATOR);
+        ISegment maxSegment = data.getElement(SegmentStoreWithRange.LAST);
+        long maxLength = Long.MAX_VALUE;
+        if (maxSegment != null) {
+            maxLength = maxSegment.getLength();
+        }
         double maxFactor = 1.0 / (maxLength + 1.0);
         long minX = Long.MAX_VALUE;
         for (ISegment segment : data) {
@@ -192,7 +192,7 @@ public abstract class AbstractSegmentStoreDensityViewer extends TmfViewer {
     public void select(Range durationRange) {
         computeDataAsync(fCurrentTimeRange, durationRange).thenAccept((data) -> {
             for (ISegmentStoreDensityViewerDataListener listener : fListeners) {
-                listener.dataSelectionChanged(data);
+                listener.selectedDataChanged(data);
             }
         });
     }
@@ -207,11 +207,11 @@ public abstract class AbstractSegmentStoreDensityViewer extends TmfViewer {
         computeDataAsync(fCurrentTimeRange, durationRange).thenAccept((data) -> applyData(data));
     }
 
-    private CompletableFuture<@Nullable List<ISegment>> computeDataAsync(final TmfTimeRange timeRange, final Range durationRange) {
+    private CompletableFuture<@Nullable SegmentStoreWithRange<ISegment>> computeDataAsync(final TmfTimeRange timeRange, final Range durationRange) {
         return CompletableFuture.supplyAsync(() -> computeData(timeRange, durationRange));
     }
 
-    private @Nullable List<ISegment> computeData(final TmfTimeRange timeRange, final Range durationRange) {
+    private @Nullable SegmentStoreWithRange<ISegment> computeData(final TmfTimeRange timeRange, final Range durationRange) {
         final ISegmentStoreProvider segmentProvider = fSegmentStoreProvider;
         if (segmentProvider == null) {
             return null;
@@ -221,27 +221,27 @@ public abstract class AbstractSegmentStoreDensityViewer extends TmfViewer {
             return null;
         }
 
-        Iterator<ISegment> intersectingElements = segStore.getIntersectingElements(timeRange.getStartTime().getValue(), timeRange.getEndTime().getValue()).iterator();
-
+        // Filter on the segment duration if necessary
         if (durationRange.lower > Double.MIN_VALUE || durationRange.upper < Double.MAX_VALUE) {
-            Predicate<? super ISegment> predicate = new Predicate<ISegment>() {
+            Predicate<ISegment> predicate = new Predicate<ISegment>() {
                 @Override
-                public boolean apply(@Nullable ISegment input) {
-                    return input != null && input.getLength() >= durationRange.lower && input.getLength() <= durationRange.upper;
+                public boolean test(@NonNull ISegment segment) {
+                    return segment.getLength() >= durationRange.lower && segment.getLength() <= durationRange.upper;
                 }
             };
-            intersectingElements = Iterators.filter(intersectingElements, predicate);
+            return new SegmentStoreWithRange<>(segStore, timeRange, predicate);
         }
 
-        return Lists.newArrayList(intersectingElements);
+        return new SegmentStoreWithRange<>(segStore, timeRange);
+
     }
 
-    private void applyData(final @Nullable List<ISegment> data) {
+    private void applyData(final @Nullable SegmentStoreWithRange<ISegment> data) {
         if (data != null) {
-            Collections.sort(data, SegmentComparators.INTERVAL_LENGTH_COMPARATOR);
+            data.setComparator(SegmentComparators.INTERVAL_LENGTH_COMPARATOR);
             Display.getDefault().asyncExec(() -> updateDisplay(data));
             for (ISegmentStoreDensityViewerDataListener l : fListeners) {
-                l.dataChanged(data);
+                l.viewDataChanged(data);
             }
         }
     }
index 2089753e74843b8b2ff952a46527b1dffd7dbb9d..98a0a673d498d9cfe3caebcef974bc3630e632b6 100644 (file)
@@ -24,7 +24,9 @@ public interface ISegmentStoreDensityViewerDataListener {
      *
      * @param newData
      *            the new data
+     * @deprecated Use {@link #viewDataChanged(Iterable)} instead
      */
+    @Deprecated
     void dataChanged(List<ISegment> newData);
 
     /**
@@ -32,6 +34,30 @@ public interface ISegmentStoreDensityViewerDataListener {
      *
      * @param newSelectionData
      *            the new selection of the data
+     * @deprecated Use {@link #selectedDataChanged(Iterable)} instead
      */
+    @Deprecated
     void dataSelectionChanged(@Nullable List<ISegment> newSelectionData);
+
+    /**
+     * Notification that the data changed in the viewer.
+     *
+     * @param newData
+     *            the new data
+     * @since 1.4
+     */
+    default void viewDataChanged(Iterable<? extends ISegment> newData) {
+        // To be implemented by children
+    }
+
+    /**
+     * Notification that the selection of the data changed in the viewer.
+     *
+     * @param newSelectionData
+     *            the new selection of the data
+     * @since 1.4
+     */
+    default void selectedDataChanged(@Nullable Iterable<? extends ISegment> newSelectionData) {
+        // To be implemented in children
+    }
 }
index 060173f3b4a9eed612cec163ec405948574eeecd..f5c4d9ab5a27747164759db3a6ba40029d070617 100644 (file)
@@ -269,7 +269,7 @@ public abstract class AbstractSegmentStoreTableViewer extends TmfSimpleTableView
                     addPackListener();
                     tableViewer.setInput(dataInput);
                     SegmentStoreContentProvider contentProvider = (SegmentStoreContentProvider) getTableViewer().getContentProvider();
-                    tableViewer.setItemCount(contentProvider.getSegmentCount());
+                    tableViewer.setItemCount((int) Math.min(Integer.MAX_VALUE, contentProvider.getSegmentCount()));
                 }
             }
         });
index b4be0b590909f5bdf2cfd0e6a3a30cde6cefbb23..ff818f8846923d8457b1f896598750cb8e14c12b 100644 (file)
 
 package org.eclipse.tracecompass.internal.analysis.timing.ui.views.segmentstore.table;
 
-import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNullContents;
-
-import java.util.Arrays;
-import java.util.Collection;
 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;
@@ -37,9 +38,181 @@ import com.google.common.collect.Iterables;
 public class SegmentStoreContentProvider implements ISortingLazyContentProvider {
 
     /**
-     * Array of all the segments in the segment store of the current trace
+     * 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
      */
-    private ISegment @Nullable [] fSegmentArray = null;
+    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
@@ -50,19 +223,21 @@ public class SegmentStoreContentProvider implements ISortingLazyContentProvider
      * Segment comparator
      */
     private @Nullable Comparator<ISegment> fComparator = null;
+    private @Nullable SegmentStoreWithRange<?> fStore;
 
     @Override
     public void updateElement(int index) {
         final TableViewer tableViewer = fTableViewer;
-        final ISegment @Nullable [] segmentArray = fSegmentArray;
-        if (tableViewer != null && segmentArray != null) {
-            tableViewer.replace(segmentArray[index], index);
+        SegmentStoreWithRange<?> store = fStore;
+        if (tableViewer == null || store == null) {
+            return;
         }
+        tableViewer.replace(store.getElement(index), index);
     }
 
     @Override
     public void dispose() {
-        fSegmentArray = null;
+        fStore = null;
         fTableViewer = null;
         fComparator = null;
     }
@@ -70,47 +245,38 @@ public class SegmentStoreContentProvider implements ISortingLazyContentProvider
     @Override
     public void inputChanged(@Nullable Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) {
         fTableViewer = (TableViewer) viewer;
-        if (newInput instanceof Collection<?> || newInput instanceof ISegmentStore) {
-            @SuppressWarnings("unchecked")
-            Collection<ISegment> segmentStore = (Collection<@NonNull ISegment>) newInput;
-            ISegment[] array = Iterables.toArray(segmentStore, ISegment.class);
-            @NonNull ISegment[] checkedArray = checkNotNullContents(array);
-            if (fComparator != null) {
-                Arrays.sort(checkedArray, fComparator);
+        if (newInput instanceof SegmentStoreWithRange) {
+            SegmentStoreWithRange<?> sswr = (SegmentStoreWithRange<?>) newInput;
+            Comparator<ISegment> comparator = fComparator;
+            if (comparator != null) {
+                sswr.setComparator(comparator);
             }
-            fSegmentArray = checkedArray;
-        } else if (newInput instanceof ISegment[]) {
-            /*
-             * Ensure that there are no null elements in the array, so we can
-             * set it back to fSegmentArray, which does not allow nulls.
-             */
-            @NonNull ISegment[] checkedArray = checkNotNullContents((@Nullable ISegment[]) newInput);
-            if (fComparator != null) {
-                Arrays.sort(checkedArray, fComparator);
+            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);
             }
-            fSegmentArray = checkedArray;
+            fStore = sswr;
         } else {
-            fSegmentArray = null;
+            fStore = null;
         }
     }
 
     @Override
     public void setSortOrder(@Nullable Comparator<?> comparator) {
-        @NonNull ISegment @Nullable [] segmentArray = fSegmentArray;
-        if (comparator == null) {
-            return;
-        }
-        if (segmentArray == null) {
-            return;
-        }
+        SegmentStoreWithRange<?> store = fStore;
         final TableViewer tableViewer = fTableViewer;
-        if (tableViewer == null) {
+        if (comparator == null || store == null || tableViewer == null) {
             return;
         }
         @SuppressWarnings("unchecked")
         Comparator<ISegment> comp = (Comparator<ISegment>) comparator;
         fComparator = comp;
-        Arrays.sort(segmentArray, fComparator);
+        store.setComparator(comp);
         tableViewer.refresh();
     }
 
@@ -119,8 +285,8 @@ public class SegmentStoreContentProvider implements ISortingLazyContentProvider
      *
      * @return the segment count
      */
-    public int getSegmentCount() {
-        ISegment[] segmentArray = fSegmentArray;
-        return (segmentArray == null ? 0 : segmentArray.length);
+    public long getSegmentCount() {
+        SegmentStoreWithRange<?> store = fStore;
+        return (store == null ? 0 : store.getSegmentCount());
     }
 }
diff --git a/common/org.eclipse.tracecompass.common.core/annotations/com/google/common/collect/Iterables.eea b/common/org.eclipse.tracecompass.common.core/annotations/com/google/common/collect/Iterables.eea
new file mode 100644 (file)
index 0000000..a7bb9c8
--- /dev/null
@@ -0,0 +1,4 @@
+class com/google/common/collect/Iterables
+filter
+ <T:Ljava/lang/Object;>(Ljava/lang/Iterable<TT;>;Lcom/google/common/base/Predicate<-TT;>;)Ljava/lang/Iterable<TT;>;
+ <T:Ljava/lang/Object;>(Ljava/lang/Iterable<TT;>;Lcom/google/common/base/Predicate<-TT;>;)L1java/lang/Iterable<TT;>;
diff --git a/common/org.eclipse.tracecompass.common.core/annotations/java/util/Comparator.eea b/common/org.eclipse.tracecompass.common.core/annotations/java/util/Comparator.eea
new file mode 100644 (file)
index 0000000..56a6433
--- /dev/null
@@ -0,0 +1,4 @@
+class java/util/Comparator
+reversed
+ ()Ljava/util/Comparator<TT;>;
+ ()L1java/util/Comparator<TT;>;
index 0a57f1bb8b55c8f7cbd21e0c2b44f4a3b71932bc..677615d5983853bccf00d993bc4348d6183ac42f 100644 (file)
@@ -17,10 +17,10 @@ import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
 
-import com.google.common.collect.Lists;
-
 import org.eclipse.jdt.annotation.Nullable;
 
+import com.google.common.collect.Lists;
+
 /**
  * Interface for segment-storing backends.
  *
This page took 0.037215 seconds and 5 git commands to generate.