common: Introduce a generic Stream flattener utility
authorAlexandre Montplaisir <alexmonthy@efficios.com>
Tue, 24 Nov 2015 23:50:06 +0000 (18:50 -0500)
committerAlexandre Montplaisir <alexmonthy@efficios.com>
Fri, 27 Nov 2015 22:06:40 +0000 (17:06 -0500)
This can be used to flatten data structures like trees, without
having to mess with recursive functions or visitor objects.

Change-Id: I3a30484a26f9112c624705c03e49529f5abac26e
Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
Reviewed-on: https://git.eclipse.org/r/59293
Reviewed-by: Hudson CI
Reviewed-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Tested-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/collect/AllTests.java
common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/collect/StreamFlattenerTest.java [new file with mode: 0644]
common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/collect/StreamFlattener.java [new file with mode: 0644]

index f98998b19dc5a25b9bc359b270d1b3b5cd79aa98..cbd75b8b2d5414ec8df7651ae6f1f1de8dd034f2 100644 (file)
@@ -21,7 +21,8 @@ import org.junit.runners.Suite;
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-    BufferedBlockingQueueTest.class
+    BufferedBlockingQueueTest.class,
+    StreamFlattenerTest.class
 })
 public class AllTests {
 
diff --git a/common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/collect/StreamFlattenerTest.java b/common/org.eclipse.tracecompass.common.core.tests/src/org/eclipse/tracecompass/common/core/tests/collect/StreamFlattenerTest.java
new file mode 100644 (file)
index 0000000..b2949fb
--- /dev/null
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2015 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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.common.core.tests.collect;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.tracecompass.common.core.collect.StreamFlattener;
+import org.junit.Test;
+
+/**
+ * Test for {@link StreamFlattener}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class StreamFlattenerTest {
+
+    /**
+     * Test flattening a tree.
+     *
+     * Each node has a String value, and they will be organized as:
+     *
+     * <pre>
+     *     A
+     *    / \
+     *   B   C
+     *  /|   |\
+     * D E   F G
+     * </pre>
+     *
+     * In-order, depth-first visiting should give the following sequence:<br>
+     * A -> B -> D -> E -> C -> F -> G
+     */
+    @Test
+    public void testFlattenTree() {
+        /* Prepare the tree */
+        TreeNode nodeD = new TreeNode(new TreeNode[0], "D");
+        TreeNode nodeE = new TreeNode(new TreeNode[0], "E");
+        TreeNode nodeF = new TreeNode(new TreeNode[0], "F");
+        TreeNode nodeG = new TreeNode(new TreeNode[0], "G");
+        TreeNode nodeB = new TreeNode(new TreeNode[] {nodeD, nodeE}, "B");
+        TreeNode nodeC = new TreeNode(new TreeNode[] {nodeF, nodeG}, "C");
+        TreeNode nodeA = new TreeNode(new TreeNode[] {nodeB, nodeC}, "A");
+
+        /* Run the test */
+        StreamFlattener<TreeNode> sf = new StreamFlattener<>(node -> Arrays.stream(node.getChildren()));
+
+        List<String> expected = Arrays.asList("A", "B", "D", "E", "C", "F", "G");
+        List<String> results = sf.flatten(nodeA).map(TreeNode::getValue).collect(Collectors.toList());
+
+        assertEquals(expected, results);
+    }
+
+    private static class TreeNode {
+
+        private final TreeNode[] children;
+        private final String value;
+
+        public TreeNode(TreeNode[] children, String value) {
+            this.children = children;
+            this.value = value;
+        }
+
+        public TreeNode[] getChildren() {
+            return children;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+}
diff --git a/common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/collect/StreamFlattener.java b/common/org.eclipse.tracecompass.common.core/src/org/eclipse/tracecompass/common/core/collect/StreamFlattener.java
new file mode 100644 (file)
index 0000000..af7845b
--- /dev/null
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2015 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * 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.common.core.collect;
+
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+/**
+ * Generic utility class to "flatten" a data structure using the {@link Stream}
+ * API.
+ *
+ * @author Alexandre Montplaisir
+ *
+ * @param <T>
+ *            The type of container, or "node" in the tree
+ * @since 2.0
+ */
+public class StreamFlattener<T> {
+
+    private final Function<T, Stream<T>> fGetChildrenFunction;
+
+    /**
+     * Constructor
+     *
+     * @param getChildrenFunction
+     *            The function to use to get each element's children. Should
+     *            return a {@link Stream} of those children.
+     */
+    public StreamFlattener(Function<T, Stream<T>> getChildrenFunction) {
+        fGetChildrenFunction = getChildrenFunction;
+    }
+
+    /**
+     * Do an in-order flattening of the data structure, starting at the given
+     * element (or node).
+     *
+     * @param element
+     *            The tree node or similar from which to start
+     * @return A unified Stream of all the children that were found,
+     *         recursively.
+     */
+    public Stream<T> flatten(T element) {
+        return Stream.concat(
+                Stream.of(element),
+                fGetChildrenFunction.apply(element).flatMap(this::flatten));
+    }
+}
This page took 0.0267 seconds and 5 git commands to generate.