datastore: Add an overlapping history tree
authorLoïc Prieur-Drevon <loic.prieurdrevon@gmail.com>
Fri, 25 Nov 2016 20:40:58 +0000 (15:40 -0500)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Thu, 2 Feb 2017 14:04:04 +0000 (09:04 -0500)
This adds a generic implementation of an history tree where children
nodes are allowed to overlap their ranges.

Change-Id: Ie5f8de62924361430c5d5b38e8ed1fbbc6d755dc
Signed-off-by: Loïc Prieur-Drevon <loic.prieurdrevon@gmail.com>
Signed-off-by: Geneviève Bastien <gbastien+lttng@versatic.net>
Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
Reviewed-on: https://git.eclipse.org/r/85801
Reviewed-by: Hudson CI
13 files changed:
statesystem/org.eclipse.tracecompass.datastore.core.tests/META-INF/MANIFEST.MF
statesystem/org.eclipse.tracecompass.datastore.core.tests/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/HTCoreNodeTest.java
statesystem/org.eclipse.tracecompass.datastore.core.tests/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/HTNodeTest.java
statesystem/org.eclipse.tracecompass.datastore.core.tests/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/AbstractOverlappingHistoryTreeTestBase.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.datastore.core.tests/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTreeTest.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.datastore.core.tests/stubs/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTreeStub.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.datastore.core.tests/stubs/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/package-info.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.datastore.core/META-INF/MANIFEST.MF
statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/AbstractHistoryTree.java
statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/AbstractOverlappingHistoryTree.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTree.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingNode.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/package-info.java [new file with mode: 0644]

index c83a60fe3e0f2d9942fecb226cbd424628866fbe..61f9efdd967ae82e09478c4c8512f6c050dac659 100644 (file)
@@ -13,5 +13,6 @@ Export-Package: org.eclipse.tracecompass.internal.datastore.core.condition;x-int
  org.eclipse.tracecompass.internal.datastore.core.serialization;x-internal:=true,
  org.eclipse.tracecompass.internal.provisional.datastore.core.condition;x-internal:=true,
  org.eclipse.tracecompass.internal.provisional.datastore.core.historytree;x-internal:=true,
- org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic;x-internal:=true
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic;x-internal:=true,
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;x-internal:=true
 Import-Package: com.google.common.collect
index b2f75be50ef8e6d484fdbb5874de7fc6be869182..5360b5b2aabf241625d03f71ad4d6f04b9ae1170 100644 (file)
@@ -21,6 +21,7 @@ import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.
 import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HistoryTreeStub;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode.NodeType;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic.ClassicHistoryTreeStub;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingHistoryTreeStub;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
 import org.junit.Test;
@@ -59,8 +60,14 @@ public class HTCoreNodeTest<E extends IHTInterval, N extends HTNode<E>> extends
                         HTNode.COMMON_HEADER_SIZE + Integer.BYTES + Integer.BYTES * NB_CHILDREN + Long.BYTES * NB_CHILDREN,
                         ClassicHistoryTreeStub.CLASSIC_NODE_FACTORY,
                         HtTestUtils.READ_FACTORY,
-                        HTNodeTest.BASE_OBJ_FACTORY },
-
+                        HTNodeTest.BASE_OBJ_FACTORY
+                },
+                { "Overlapping core node",
+                        HTNode.COMMON_HEADER_SIZE + Integer.BYTES + Integer.BYTES * NB_CHILDREN + 2 * Long.BYTES * NB_CHILDREN,
+                        OverlappingHistoryTreeStub.OVERLAPPING_NODE_FACTORY,
+                        HtTestUtils.READ_FACTORY,
+                        HTNodeTest.BASE_OBJ_FACTORY
+                },
         });
     }
 
index af27fd72b27f4cdd03e7ecef295c961765d9130d..83cad053e1f81722288831b5b9fed6aefdcfbb16 100644 (file)
@@ -25,6 +25,7 @@ import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.
 import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode.NodeType;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic.ClassicHistoryTreeStub;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingHistoryTreeStub;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.HTInterval;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
 import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
@@ -92,7 +93,14 @@ public class HTNodeTest<E extends IHTInterval, N extends HTNode<E>> {
                         HTNode.COMMON_HEADER_SIZE,
                         ClassicHistoryTreeStub.CLASSIC_NODE_FACTORY,
                         HtTestUtils.READ_FACTORY,
-                        BASE_OBJ_FACTORY },
+                        BASE_OBJ_FACTORY
+                },
+                { "Overlapping leaf node",
+                        HTNode.COMMON_HEADER_SIZE,
+                        OverlappingHistoryTreeStub.OVERLAPPING_NODE_FACTORY,
+                        HtTestUtils.READ_FACTORY,
+                        BASE_OBJ_FACTORY
+                },
         });
     }
 
diff --git a/statesystem/org.eclipse.tracecompass.datastore.core.tests/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/AbstractOverlappingHistoryTreeTestBase.java b/statesystem/org.eclipse.tracecompass.datastore.core.tests/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/AbstractOverlappingHistoryTreeTestBase.java
new file mode 100644 (file)
index 0000000..f112c75
--- /dev/null
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2017 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTreeTestBase;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HtTestUtils;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+import org.junit.Test;
+
+/**
+ * Test the overlapping history tree. This base class keeps the parameter such
+ * that any tree implementing the overlapping history tree can extend this test
+ * method
+ *
+ * @author Geneviève Bastien
+ * @param <E>
+ *            Type of interval
+ * @param <N>
+ *            Type of nodes in the tree
+ */
+public abstract class AbstractOverlappingHistoryTreeTestBase<E extends IHTInterval, N extends OverlappingNode<E>>
+        extends AbstractHistoryTreeTestBase<E, N> {
+
+    @Override
+    protected abstract AbstractOverlappingHistoryTree<E, N> createHistoryTree(File stateHistoryFile,
+            int blockSize,
+            int maxChildren,
+            int providerVersion,
+            long treeStart) throws IOException;
+
+    @Override
+    protected abstract AbstractOverlappingHistoryTree<E, N> createHistoryTree(
+            File existingStateFile,
+            int expectedProviderVersion) throws IOException;
+
+    /**
+     * Test that the children start and end times are as expected
+     */
+    @Test
+    public void testChildrenTimes() {
+        AbstractOverlappingHistoryTree<E, N> ht = (AbstractOverlappingHistoryTree<E, N>) setupSmallTree();
+
+        /* Fill a first node */
+        OverlappingNode<E> node = ht.getLatestLeaf();
+        long time = fillValues(ht, node.getNodeFreeSpace(), 1);
+
+        /* Add elements that should add a sibling to the node */
+        assertEquals(1, ht.getNodeCount());
+        assertEquals(1, ht.getDepth());
+        ht.insert(createInterval(time, time + 1));
+
+        assertEquals(3, ht.getNodeCount());
+        assertEquals(2, ht.getDepth());
+
+        // The first node should have been closed, so let's check that the
+        // parent has the right start and end end time
+        OverlappingNode<E> parent = ht.getLatestNode(0);
+        assertEquals(time, node.getNodeEnd());
+        assertEquals(time, parent.getChildEnd(0));
+        assertEquals(node.getNodeStart(), parent.getChildStart(0));
+
+        /* Fill the latest leaf node (2nd child) */
+        node = ht.getLatestLeaf();
+        time += 1;
+        time = fillValues(ht, node.getNodeFreeSpace(), time);
+
+        /*
+         * Add an element that should add another sibling to the previous nodes
+         */
+        ht.insert(createInterval(time, time + 1));
+        assertEquals(4, ht.getNodeCount());
+        assertEquals(2, ht.getDepth());
+
+        // The second node should have been closed, so let's check that the
+        // parent has the right start and end time
+        assertEquals(time, node.getNodeEnd());
+        assertEquals(time, parent.getChildEnd(1));
+        assertEquals(node.getNodeStart(), parent.getChildStart(1));
+
+        /* Fill the latest leaf node (3rd and last child) */
+        node = ht.getLatestLeaf();
+        time += 1;
+        time = fillValues(ht, node.getNodeFreeSpace(), time);
+
+        /* The new node created here should generate a new branch */
+        ht.insert(createInterval(time, time + 1));
+        assertEquals(7, ht.getNodeCount());
+        assertEquals(3, ht.getDepth());
+
+        // The third node and previous parent should have been closed, so let's
+        // check that the parent has the right start and  end time
+        assertEquals(time, node.getNodeEnd());
+        assertEquals(time, parent.getChildEnd(2));
+        assertEquals(node.getNodeStart(), parent.getChildStart(2));
+
+        // Now verify the higher level
+        node = parent;
+        parent = ht.getLatestNode(0);
+        assertEquals(time, node.getNodeEnd());
+        assertEquals(time, parent.getChildEnd(0));
+
+        // Assert the integrity of the tree
+        ht.closeTree(ht.getTreeEnd());
+        time = ht.getTreeEnd();
+
+        /* The tree is closed, the last branch should have their end time set */
+        // leaf level
+        assertEquals(time, ht.getLatestNode(2).getNodeEnd());
+        // Second level
+        assertEquals(time, ht.getLatestNode(1).getNodeEnd());
+        assertEquals(time, ht.getLatestNode(1).getChildEnd(0));
+        // Head level
+        assertEquals(time, parent.getNodeEnd());
+        assertEquals(time, parent.getChildEnd(1));
+
+        HtTestUtils.assertTreeIntegrity(ht);
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.datastore.core.tests/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTreeTest.java b/statesystem/org.eclipse.tracecompass.datastore.core.tests/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTreeTest.java
new file mode 100644 (file)
index 0000000..551f25f
--- /dev/null
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2017 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTree;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTreeTestBase;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HtTestUtils;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.HTInterval;
+import org.junit.Test;
+
+/**
+ * Tests for the concrete {@link OverlappingHistoryTree} class.
+ *
+ * @author Geneviève Bastien
+ */
+public class OverlappingHistoryTreeTest
+        extends AbstractHistoryTreeTestBase<HTInterval, OverlappingNode<HTInterval>> {
+
+    private static final HTInterval DEFAULT_OBJECT = new HTInterval(0, 0);
+
+    @Override
+    protected OverlappingHistoryTreeStub createHistoryTree(File stateHistoryFile,
+            int blockSize,
+            int maxChildren,
+            int providerVersion,
+            long treeStart) throws IOException {
+
+        return new OverlappingHistoryTreeStub(stateHistoryFile,
+                blockSize,
+                maxChildren,
+                providerVersion,
+                treeStart);
+    }
+
+    @Override
+    protected OverlappingHistoryTreeStub createHistoryTree(@NonNull File existingStateFile,
+            int expectedProviderVersion) throws IOException {
+        return new OverlappingHistoryTreeStub(existingStateFile, expectedProviderVersion);
+    }
+
+    @Override
+    protected HTInterval createInterval(long start, long end) {
+        return new HTInterval(start, end);
+    }
+
+    @Override
+    protected long fillValues(
+            AbstractHistoryTree<HTInterval, OverlappingNode<HTInterval>> ht,
+            int fillSize, long start) {
+
+        int nbValues = fillSize / DEFAULT_OBJECT.getSizeOnDisk();
+        for (int i = 0; i < nbValues; i++) {
+            ht.insert(new HTInterval(start + i, start + i + 1));
+        }
+        return start + nbValues;
+    }
+
+    /**
+     * Test insertions at different moments
+     */
+    @Test
+    public void testInsertions() {
+        long start = 10L;
+        final OverlappingHistoryTreeStub ht = (OverlappingHistoryTreeStub) setupSmallTree(3, start);
+
+        /* Fill a first node */
+        OverlappingNode<HTInterval> node = ht.getLatestLeaf();
+        long time = fillValues(ht, node.getNodeFreeSpace(), start);
+
+        /*
+         * Add an element that starts before the last end time and make sure it
+         * is added to the next node
+         */
+        long lastStart = time - 10;
+        assertEquals(1, ht.getNodeCount());
+        assertEquals(1, ht.getDepth());
+        ht.insert(createInterval(lastStart, time + 1));
+
+        assertEquals(3, ht.getNodeCount());
+        assertEquals(2, ht.getDepth());
+        assertEquals(1, ht.getLastInsertionLocation());
+
+        /*
+         * Add an element that starts before the node start and make sure it is
+         * added in the parent
+         */
+        ht.insert(createInterval(lastStart - 5, time + 1));
+        assertEquals(0, ht.getLastInsertionLocation());
+
+        /* Fill the latest leaf node (2nd child) */
+        node = ht.getLatestLeaf();
+        time += 1;
+        time = fillValues(ht, node.getNodeFreeSpace(), time);
+
+        /*
+         * Add an interval that should add another sibling to the previous
+         * nodes, but that starts before last node. The sibling should not start
+         * before previous one, so the element will be added at the top
+         */
+        ht.insert(createInterval(lastStart - 5, time + 1));
+        assertEquals(4, ht.getNodeCount());
+        assertEquals(2, ht.getDepth());
+        assertEquals(0, ht.getLastInsertionLocation());
+
+        OverlappingNode<HTInterval> newNode = ht.getLatestLeaf();
+        assertTrue(node.getSequenceNumber() != newNode.getSequenceNumber());
+        assertEquals(lastStart, newNode.getNodeStart());
+
+        // Fill the last node with values in the past and make sure there are no
+        // new nodes
+        node = ht.getLatestLeaf();
+        time = fillValues(ht, node.getNodeFreeSpace(), node.getNodeStart());
+        assertEquals(4, ht.getNodeCount());
+        assertEquals(2, ht.getDepth());
+
+        // Add an interval that will create a new branch
+        ht.insert(createInterval(time, time + 1));
+        assertEquals(7, ht.getNodeCount());
+        assertEquals(3, ht.getDepth());
+        assertEquals(2, ht.getLastInsertionLocation());
+
+        // Fill the last leaf node
+        node = ht.getLatestLeaf();
+        time = fillValues(ht, node.getNodeFreeSpace(), time);
+
+        // Add an element that starts at the beginning and make sure it is
+        // inserted at the top level.
+        ht.insert(createInterval(start + 1, time));
+        assertEquals(0, ht.getLastInsertionLocation());
+
+        // An element with the correct start/end should create a sibling now
+        ht.insert(createInterval(time, time + 1));
+        assertEquals(8, ht.getNodeCount());
+
+        // Close the tree and assert integrity
+        ht.closeTree(ht.getTreeEnd());
+
+        HtTestUtils.assertTreeIntegrity(ht);
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.datastore.core.tests/stubs/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTreeStub.java b/statesystem/org.eclipse.tracecompass.datastore.core.tests/stubs/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTreeStub.java
new file mode 100644 (file)
index 0000000..4578dd5
--- /dev/null
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2017 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingHistoryTree;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping.OverlappingNode;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.HTInterval;
+
+/**
+ * A stub for the overlapping history tree, specifying the object type to
+ * {@link HTInterval}.
+ *
+ * @author Geneviève Bastien
+ */
+public class OverlappingHistoryTreeStub extends OverlappingHistoryTree<HTInterval> {
+
+    /**
+     * A factory to create leaf and core nodes based on the BaseHtObject object
+     */
+    public static final IHTNodeFactory<HTInterval, OverlappingNode<HTInterval>> OVERLAPPING_NODE_FACTORY =
+            (t, b, m, seq, p, start) -> new OverlappingNode<>(t, b, m, seq, p, start);
+
+    private int fLastInsertionIndex;
+
+    /**
+     * Create a new Overlapping History Tree test stub from scratch, specifying
+     * all configuration parameters.
+     *
+     * @param stateHistoryFile
+     *            The name of the history file
+     * @param blockSize
+     *            The size of each "block" on disk in bytes. One node will
+     *            always fit in one block. It should be at least 4096.
+     * @param maxChildren
+     *            The maximum number of children allowed per core (non-leaf)
+     *            node.
+     * @param providerVersion
+     *            The version of the state provider. If a file already exists,
+     *            and their versions match, the history file will not be rebuilt
+     *            uselessly.
+     * @param treeStart
+     *            The start time of the history
+     * @throws IOException
+     *             If an error happens trying to open/write to the file
+     *             specified in the config
+     */
+    public OverlappingHistoryTreeStub(File stateHistoryFile,
+            int blockSize,
+            int maxChildren,
+            int providerVersion,
+            long treeStart) throws IOException {
+
+        super(stateHistoryFile,
+                blockSize,
+                maxChildren,
+                providerVersion,
+                treeStart,
+                HTInterval.INTERVAL_READER);
+    }
+
+    /**
+     * "Reader" constructor : instantiate a SHTree from an existing tree file on
+     * disk
+     *
+     * @param existingStateFile
+     *            Path/filename of the history-file we are to open
+     * @param expectedProviderVersion
+     *            The expected version of the state provider
+     * @throws IOException
+     *             If an error happens reading the file
+     */
+    public OverlappingHistoryTreeStub(File existingStateFile,
+            int expectedProviderVersion) throws IOException {
+        super(existingStateFile, expectedProviderVersion, HTInterval.INTERVAL_READER);
+    }
+
+    @Override
+    protected IHTNodeFactory<HTInterval, OverlappingNode<HTInterval>> getNodeFactory() {
+        return OVERLAPPING_NODE_FACTORY;
+    }
+
+    @Override
+    protected void informInsertingAtDepth(int depth) {
+        fLastInsertionIndex = depth;
+    }
+
+    /**
+     * Get the index in the current branch where the last element was inserted
+     *
+     * @return The index in the branch of the last insertion
+     */
+    public int getLastInsertionLocation() {
+        return fLastInsertionIndex;
+    }
+
+    // ------------------------------------------------------------------------
+    // Re-exported methods (to be visible for the tests in same package)
+    // ------------------------------------------------------------------------
+
+    @Override
+    protected OverlappingNode<HTInterval> getLatestNode(int depth) {
+        return super.getLatestNode(depth);
+    }
+
+    @Override
+    protected int getDepth() {
+        return super.getDepth();
+    }
+
+    @Override
+    protected OverlappingNode<HTInterval> getLatestLeaf() {
+        return super.getLatestLeaf();
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.datastore.core.tests/stubs/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/package-info.java b/statesystem/org.eclipse.tracecompass.datastore.core.tests/stubs/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/package-info.java
new file mode 100644 (file)
index 0000000..e237aea
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2017 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
index c3c64fb064bbbb327888cbe39d603d1b9262868d..ac403ae469ad4df2c12a14516ab4098a5b2c0a73 100644 (file)
@@ -18,6 +18,7 @@ Export-Package: org.eclipse.tracecompass.internal.datastore.core;x-internal:=tru
  org.eclipse.tracecompass.internal.provisional.datastore.core.exceptions,
  org.eclipse.tracecompass.internal.provisional.datastore.core.historytree;x-internal:=true,
  org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.classic;x-internal:=true,
+ org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;x-internal:=true,
  org.eclipse.tracecompass.internal.provisional.datastore.core.interval;x-internal:=true,
  org.eclipse.tracecompass.internal.provisional.datastore.core.serialization;x-friends:="org.eclipse.tracecompass.statesystem.core,org.eclipse.tracecompass.statesystem.core.tests"
 Import-Package: com.google.common.annotations,
index 0f6d47b9dc94cc7352e5443d20e9afc14b3a3706..19e1228798f3acfa4ae3998255d40961f49c9f9e 100644 (file)
@@ -375,7 +375,7 @@ public abstract class AbstractHistoryTree<E extends IHTInterval, N extends HTNod
      *            The depth at which to get the node
      * @return The node at depth
      */
-    protected final N getLatestNode(int depth) {
+    protected N getLatestNode(int depth) {
         if (depth > fLatestBranch.size()) {
             throw new IndexOutOfBoundsException("Trying to get latest node too deep"); //$NON-NLS-1$
         }
@@ -634,6 +634,7 @@ public abstract class AbstractHistoryTree<E extends IHTInterval, N extends HTNod
      */
     protected final void tryInsertAtNode(E interval, int depth) {
         N targetNode = getLatestBranch().get(depth);
+        informInsertingAtDepth(depth);
 
         /* Verify if there is enough room in this node to store this interval */
         if (interval.getSizeOnDisk() > targetNode.getNodeFreeSpace()) {
@@ -662,6 +663,22 @@ public abstract class AbstractHistoryTree<E extends IHTInterval, N extends HTNod
         updateEndTime(interval);
     }
 
+    /**
+     * Informs the tree that the insertion is requested at a given depth. When
+     * this is called, the element is not yet inserted, but the last call to
+     * this for an element will represent the depth at which is was really
+     * inserted. By default, this method does nothing and should not be
+     * necessary for concrete implementations, but it can be used by unit tests
+     * to check to position of insertion of elements.
+     *
+     * @param depth
+     *            The depth at which the last insertion was done
+     */
+    @VisibleForTesting
+    protected void informInsertingAtDepth(int depth) {
+
+    }
+
     /**
      * Get the start time of the new node of the branch that will be added
      * starting at depth.
diff --git a/statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/AbstractOverlappingHistoryTree.java b/statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/AbstractOverlappingHistoryTree.java
new file mode 100644 (file)
index 0000000..502d534
--- /dev/null
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2017 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.RangeCondition;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.AbstractHistoryTree;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * Overlapping history tree, where children node's ranges are allowed to overlap
+ * and children will start at the time of the next interval to insert or at
+ * minimum at the time of its sibling node
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ * @param <E>
+ *            The type of objects that will be saved in the tree
+ * @param <N> The type of node used by this tree
+ */
+public abstract class AbstractOverlappingHistoryTree<E extends IHTInterval, N extends OverlappingNode<E>>
+        extends AbstractHistoryTree<E, N> {
+
+    // ------------------------------------------------------------------------
+    // Constructors/"Destructors"
+    // ------------------------------------------------------------------------
+
+    /**
+     * Create a new Overlapping History Tree from scratch, specifying all
+     * configuration parameters.
+     *
+     * @param stateHistoryFile
+     *            The name of the history file
+     * @param blockSize
+     *            The size of each "block" on disk in bytes. One node will
+     *            always fit in one block. It should be at least 4096.
+     * @param maxChildren
+     *            The maximum number of children allowed per core (non-leaf)
+     *            node.
+     * @param providerVersion
+     *            The version of the state provider. If a file already exists,
+     *            and their versions match, the history file will not be rebuilt
+     *            uselessly.
+     * @param treeStart
+     *            The start time of the history
+     * @param intervalReader
+     *            The factory to create new tree data elements when reading from
+     *            the disk
+     * @throws IOException
+     *             If an error happens trying to open/write to the file
+     *             specified in the config
+     */
+    public AbstractOverlappingHistoryTree(File stateHistoryFile,
+            int blockSize,
+            int maxChildren,
+            int providerVersion,
+            long treeStart,
+            IHTIntervalReader<E> intervalReader) throws IOException {
+
+        super(stateHistoryFile,
+                blockSize,
+                maxChildren,
+                providerVersion,
+                treeStart,
+                intervalReader);
+    }
+
+    /**
+     * "Reader" constructor : instantiate a SHTree from an existing tree file on
+     * disk
+     *
+     * @param existingStateFile
+     *            Path/filename of the history-file we are to open
+     * @param expectedProviderVersion
+     *            The expected version of the state provider
+     * @param objectReader
+     *            The factory used to read segments from the history tree
+     * @throws IOException
+     *             If an error happens reading the file
+     */
+    public AbstractOverlappingHistoryTree(File existingStateFile,
+            int expectedProviderVersion,
+            IHTIntervalReader<E> objectReader) throws IOException {
+        super(existingStateFile, expectedProviderVersion, objectReader);
+    }
+
+    @Override
+    protected long getNewBranchStart(int depth, E interval) {
+        // The node starts at the time of the interval to add, but should not
+        // start before the previous sibling
+        // TODO: Do some benchmark to see if this Math.max is efficient enough
+        // as opposed to just interval.getStart which would require to start the
+        // new branch higher up in the tree
+        return Math.max(interval.getStart(), getLatestNode(depth).getNodeStart());
+    }
+
+    // ------------------------------------------------------------------------
+    // Test-specific methods
+    // ------------------------------------------------------------------------
+
+    @Override
+    @VisibleForTesting
+    protected N getLatestLeaf() {
+        return super.getLatestLeaf();
+    }
+
+    @Override
+    @VisibleForTesting
+    protected int getDepth() {
+        return super.getDepth();
+    }
+
+    @Override
+    @VisibleForTesting
+    protected N getLatestNode(int depth) {
+        return super.getLatestNode(depth);
+    }
+
+    @Override
+    @VisibleForTesting
+    protected boolean verifyChildrenSpecific(N parent, int index, N child) {
+        if (parent.getChildStart(index) == child.getNodeStart()
+                && parent.getChildEnd(index) == child.getNodeEnd()) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    @VisibleForTesting
+    protected boolean verifyIntersectingChildren(N parent, N child) {
+        int childSequence = child.getSequenceNumber();
+        boolean shouldBeInCollection;
+        Collection<Integer> nextChildren;
+        for (long t = parent.getNodeStart(); t < parent.getNodeEnd(); t++) {
+            RangeCondition<Long> timeCondition = RangeCondition.singleton(t);
+            shouldBeInCollection = (timeCondition.intersects(child.getNodeStart(), child.getNodeEnd()));
+            nextChildren = parent.selectNextChildren(timeCondition);
+            if (shouldBeInCollection != nextChildren.contains(childSequence)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTree.java b/statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingHistoryTree.java
new file mode 100644 (file)
index 0000000..da302f2
--- /dev/null
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2017 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTIntervalReader;
+
+/**
+ * Basic implementation of {@link AbstractOverlappingHistoryTree} that can
+ * be used directly by clients.
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ * @param <E>
+ *            The type of objects that will be saved in the tree
+ */
+public class OverlappingHistoryTree<E extends IHTInterval>
+        extends AbstractOverlappingHistoryTree<E, OverlappingNode<E>> {
+
+    /**
+     * The magic number for this file format.
+     */
+    public static final int HISTORY_FILE_MAGIC_NUMBER = 0x05FFA800;
+
+    /** File format version. Increment when breaking compatibility. */
+    private static final int FILE_VERSION = 1;
+
+    private final IHTNodeFactory<E, OverlappingNode<E>> fNodeFactory =
+            (t, b, m, seq, p, start) -> new OverlappingNode<>(t, b, m, seq, p, start);
+
+    // ------------------------------------------------------------------------
+    // Constructors/"Destructors"
+    // ------------------------------------------------------------------------
+
+    /**
+     * Create a new Overlapping History Tree from scratch, specifying all
+     * configuration parameters.
+     *
+     * @param stateHistoryFile
+     *            The name of the history file
+     * @param blockSize
+     *            The size of each "block" on disk in bytes. One node will
+     *            always fit in one block. It should be at least 4096.
+     * @param maxChildren
+     *            The maximum number of children allowed per core (non-leaf)
+     *            node.
+     * @param providerVersion
+     *            The version of the state provider. If a file already exists,
+     *            and their versions match, the history file will not be rebuilt
+     *            uselessly.
+     * @param treeStart
+     *            The start time of the history
+     * @param intervalReader
+     *            The factory to create new tree data elements when reading from
+     *            the disk
+     * @throws IOException
+     *             If an error happens trying to open/write to the file
+     *             specified in the config
+     */
+    public OverlappingHistoryTree(File stateHistoryFile,
+            int blockSize,
+            int maxChildren,
+            int providerVersion,
+            long treeStart,
+            IHTIntervalReader<E> intervalReader) throws IOException {
+
+        super(stateHistoryFile,
+                blockSize,
+                maxChildren,
+                providerVersion,
+                treeStart,
+                intervalReader);
+    }
+
+    /**
+     * "Reader" constructor : instantiate a SHTree from an existing tree file on
+     * disk
+     *
+     * @param existingStateFile
+     *            Path/filename of the history-file we are to open
+     * @param expectedProviderVersion
+     *            The expected version of the state provider
+     * @param objectReader
+     *            The factory used to read segments from the history tree
+     * @throws IOException
+     *             If an error happens reading the file
+     */
+    public OverlappingHistoryTree(File existingStateFile,
+            int expectedProviderVersion,
+            IHTIntervalReader<E> objectReader) throws IOException {
+        super(existingStateFile, expectedProviderVersion, objectReader);
+    }
+
+    @Override
+    protected int getMagicNumber() {
+        return HISTORY_FILE_MAGIC_NUMBER;
+    }
+
+    @Override
+    protected int getFileVersion() {
+        return FILE_VERSION;
+    }
+
+    @Override
+    protected IHTNodeFactory<E, OverlappingNode<E>> getNodeFactory() {
+        return fNodeFactory;
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingNode.java b/statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/OverlappingNode.java
new file mode 100644 (file)
index 0000000..80ac335
--- /dev/null
@@ -0,0 +1,350 @@
+/*******************************************************************************
+ * Copyright (c) 2017 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
+
+import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.condition.RangeCondition;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.exceptions.RangeException;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.HTNode;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.IHTNode;
+import org.eclipse.tracecompass.internal.provisional.datastore.core.interval.IHTInterval;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * History tree node for overlapping history tree. Those nodes keep both the
+ * child node's start and end times
+ *
+ * @author Loic Prieur-Drevon
+ * @author Geneviève Bastien
+ *
+ * @param <E>
+ *            The type of objects that will be saved in the tree
+ */
+public class OverlappingNode<E extends IHTInterval> extends HTNode<E> {
+
+    /**
+     * Listeners for when nodes are closed, to update the child node's
+     * information.
+     */
+    private final Set<NodeClosedListener> fListeners = new HashSet<>();
+
+    /**
+     * Listener to node close operations. This can be used by parent nodes to
+     * update their children data when a node is closed.
+     */
+    @FunctionalInterface
+    protected static interface NodeClosedListener {
+
+        /**
+         * A node was just closed
+         *
+         * @param node
+         *            The node that was just closed
+         * @param endtime
+         *            The end time of the node
+         */
+        void nodeClosed(OverlappingNode<?> node, long endtime);
+    }
+
+    /**
+     * Adds the data concerning the segment nodes, start/end of children
+     */
+    protected static class OverlappingExtraData extends CoreNodeData {
+
+        private final long[] fChildStart;
+        private final long[] fChildEnd;
+
+        /**
+         * Node data constructor
+         *
+         * @param node
+         *            The node containing this extra data.
+         */
+        public OverlappingExtraData(OverlappingNode<?> node) {
+            super(node);
+            int size = node.getMaxChildren();
+            /*
+             * * We instantiate the two following arrays at full size right
+             * away, since we want to reserve that space in the node's header.
+             * "this.nbChildren" will tell us how many relevant entries there
+             * are in those tables.
+             */
+            fChildStart = new long[size];
+            fChildEnd = new long[size];
+            for (int i = 0; i < size; i++) {
+                fChildStart[i] = 0;
+                fChildEnd[i] = Long.MAX_VALUE;
+            }
+        }
+
+        @Override
+        protected OverlappingNode<?> getNode() {
+            /* Type enforced by constructor */
+            return (OverlappingNode<?>) super.getNode();
+        }
+
+        @Override
+        public void readSpecificHeader(@NonNull ByteBuffer buffer) {
+            super.readSpecificHeader(buffer);
+            int size = getNode().getMaxChildren();
+
+            for (int i = 0; i < size; i++) {
+                fChildStart[i] = buffer.getLong();
+                fChildEnd[i] = buffer.getLong();
+            }
+        }
+
+        @Override
+        protected void writeSpecificHeader(@NonNull ByteBuffer buffer) {
+            getNode().takeReadLock();
+            try {
+                super.writeSpecificHeader(buffer);
+
+                int size = getNode().getMaxChildren();
+
+                /*
+                 * Write the children array
+                 */
+                for (int i = 0; i < size; i++) {
+                    buffer.putLong(fChildStart[i]);
+                    buffer.putLong(fChildEnd[i]);
+                }
+            } finally {
+                getNode().releaseReadLock();
+            }
+        }
+
+        @Override
+        protected int getSpecificHeaderSize() {
+            int maxChildren = getNode().getMaxChildren();
+            int specificSize = super.getSpecificHeaderSize();
+            /*
+             * MAX_NB * child start and child end arrays
+             */
+            specificSize += 2 * Long.BYTES * maxChildren;
+
+            return specificSize;
+        }
+
+        @Override
+        public void linkNewChild(IHTNode<?> childNode) {
+            if (!(childNode instanceof OverlappingNode<?>)) {
+                throw new IllegalArgumentException("Adding a node that is not an overlapping node to an overlapping tree!"); //$NON-NLS-1$
+            }
+
+            getNode().takeWriteLock();
+            try {
+                super.linkNewChild(childNode);
+                final int childIndex = getNbChildren() - 1;
+
+                // We do not know the end time at this point, add a listener to
+                // update child end when the node is closed
+                ((OverlappingNode<?>) childNode).addListener((node, endtime) -> {
+                    // FIXME On who are we calling getNode() and fChildEnd here?
+                    getNode().takeWriteLock();
+                    try {
+                        fChildEnd[childIndex] = endtime;
+                    } finally {
+                        getNode().releaseWriteLock();
+                    }
+                });
+
+                fChildStart[childIndex] = childNode.getNodeStart();
+                // The child may already be closed
+                if (childNode.isOnDisk()) {
+                    fChildEnd[childIndex] = childNode.getNodeEnd();
+                }
+
+            } finally {
+                getNode().releaseWriteLock();
+            }
+        }
+
+        @Override
+        protected Collection<Integer> selectNextIndices(RangeCondition<Long> rc) {
+            OverlappingNode<?> node = getNode();
+
+            if (rc.min() < node.getNodeStart()
+                    || (node.isOnDisk() && rc.max() > node.getNodeEnd())) {
+                throw new RangeException("Requesting children outside the node's range: " + rc.toString()); //$NON-NLS-1$
+            }
+
+            node.takeReadLock();
+            try {
+                List<Integer> childList = new ArrayList<>();
+                for (int i = 0; i < getNbChildren(); i++) {
+                    if (rc.intersects(fChildStart[i], fChildEnd[i])) {
+                        childList.add(i);
+                    }
+                }
+                return childList;
+
+            } finally {
+                node.releaseReadLock();
+            }
+
+        }
+
+        /**
+         * Get the start value of a child
+         *
+         * @param index
+         *            The child index
+         * @return The start value
+         */
+        public long getChildStart(int index) {
+            getNode().takeReadLock();
+            try {
+                if (index >= getNbChildren()) {
+                    throw new IndexOutOfBoundsException("The child at index " + index + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
+                }
+                return fChildStart[index];
+            } finally {
+                getNode().releaseReadLock();
+            }
+        }
+
+        /**
+         * Get the start value of a child
+         *
+         * @param index
+         *            The child index
+         * @return The start value
+         */
+        public long getChildEnd(int index) {
+            getNode().takeReadLock();
+            try {
+                if (index >= getNbChildren()) {
+                    throw new IndexOutOfBoundsException("The child at index " + index + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
+                }
+                return fChildEnd[index];
+            } finally {
+                getNode().releaseReadLock();
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(super.hashCode(), fChildStart, fChildEnd);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+            /* super.equals already checks for null / same class */
+            OverlappingExtraData other = (OverlappingExtraData) checkNotNull(obj);
+            return (Arrays.equals(fChildStart, other.fChildStart)
+                    && Arrays.equals(fChildEnd, other.fChildEnd));
+        }
+    }
+
+    /**
+     * Constructor
+     *
+     * @param type
+     *            The type of this node
+     * @param blockSize
+     *            The size (in bytes) of a serialized node on disk
+     * @param maxChildren
+     *            The maximum allowed number of children per node
+     * @param seqNumber
+     *            The (unique) sequence number assigned to this particular node
+     * @param parentSeqNumber
+     *            The sequence number of this node's parent node
+     * @param start
+     *            The earliest timestamp stored in this node
+     */
+    public OverlappingNode(NodeType type, int blockSize, int maxChildren,
+            int seqNumber, int parentSeqNumber, long start) {
+        super(type, blockSize, maxChildren, seqNumber, parentSeqNumber, start);
+    }
+
+    @Override
+    protected @Nullable OverlappingExtraData createNodeExtraData(final NodeType type) {
+        if (type == NodeType.CORE) {
+            return new OverlappingExtraData(this);
+        }
+        return null;
+    }
+
+    @Override
+    public void add(E newInterval) {
+        super.add(newInterval);
+    }
+
+    @Override
+    public void closeThisNode(long endtime) {
+        super.closeThisNode(endtime);
+        fListeners.forEach(l -> l.nodeClosed(this, endtime));
+    }
+
+    /**
+     * The listener for this node
+     *
+     * @param listener
+     *            The update listener
+     */
+    protected void addListener(NodeClosedListener listener) {
+        fListeners.add(listener);
+    }
+
+    @Override
+    protected @Nullable OverlappingExtraData getCoreNodeData() {
+        return (OverlappingExtraData) super.getCoreNodeData();
+    }
+
+    /**
+     * Get the start value of a child of this node
+     *
+     * @param index
+     *            The index of the node to get the child start
+     * @return The child start value
+     */
+    @VisibleForTesting
+    long getChildStart(int index) {
+        OverlappingExtraData extraData = getCoreNodeData();
+        if (extraData != null) {
+            return extraData.getChildStart(index);
+        }
+        throw new UnsupportedOperationException("A leaf node does not have children"); //$NON-NLS-1$
+    }
+
+    /**
+     * Get the end value of a child of this node
+     *
+     * @param index
+     *            The index of the node to get the child end
+     * @return The child end value
+     */
+    @VisibleForTesting
+    long getChildEnd(int index) {
+        OverlappingExtraData extraData = getCoreNodeData();
+        if (extraData != null) {
+            return extraData.getChildEnd(index);
+        }
+        throw new UnsupportedOperationException("A leaf node does not have children"); //$NON-NLS-1$
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/package-info.java b/statesystem/org.eclipse.tracecompass.datastore.core/src/org/eclipse/tracecompass/internal/provisional/datastore/core/historytree/overlapping/package-info.java
new file mode 100644 (file)
index 0000000..e237aea
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2017 École Polytechnique de Montréal
+ *
+ * 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
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.provisional.datastore.core.historytree.overlapping;
This page took 0.042407 seconds and 5 git commands to generate.