changeset 6443:728100b14718

Expose Stream.getStreamFlags, with some (currently failing) tests
author briangoetz
date Mon, 19 Nov 2012 17:56:46 -0500
parents 054fd74129f6
children c54ba7563085
files src/share/classes/java/util/stream/AbstractPipeline.java src/share/classes/java/util/stream/BaseStream.java src/share/classes/java/util/stream/Spliterator.java src/share/classes/java/util/stream/StreamOpFlags.java test-ng/tests/org/openjdk/tests/java/util/stream/StreamFlagsTest.java test-ng/tests/org/openjdk/tests/java/util/stream/StreamOpFlagsTest.java
diffstat 6 files changed, 170 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/stream/AbstractPipeline.java	Mon Nov 19 13:38:57 2012 -0500
+++ b/src/share/classes/java/util/stream/AbstractPipeline.java	Mon Nov 19 17:56:46 2012 -0500
@@ -80,6 +80,7 @@
     protected final IntermediateOp<E_IN, E_OUT> op;
     protected final int depth;
     protected final SourceState<?> sourceState;
+    protected final int combinedOpFlags;
 
     /**
      * Constructor for the element source of a pipeline.
@@ -94,6 +95,7 @@
         this.op = null;
         this.depth = 0;
         this.sourceState = new SpliteratorSourceState<>(spliterator, sourceFlags, shape);
+        this.combinedOpFlags = StreamOpFlags.INITIAL_OPS_VALUE;
     }
 
     /**
@@ -107,6 +109,7 @@
         this.op = Objects.requireNonNull(op);
         this.depth = upstream.depth + 1;
         this.sourceState = upstream.sourceState;
+        this.combinedOpFlags = StreamOpFlags.combineOpFlags(upstream.combinedOpFlags, op.getOpFlags());
 
         assert upstream.getOutputShape().getStreamType() == op.inputShape().getStreamType();
         assert (upstream.depth == 0) ^ (upstream.op != null);
@@ -527,6 +530,11 @@
     }
 
     @Override
+    public int getStreamFlags() {
+        return StreamOpFlags.combineStreamFlags(sourceState.getSourceFlags(), combinedOpFlags);
+    }
+
+    @Override
     public boolean isParallel() {
         return StreamOpFlags.PARALLEL.isKnown(sourceState.getSourceFlags());
     }
--- a/src/share/classes/java/util/stream/BaseStream.java	Mon Nov 19 13:38:57 2012 -0500
+++ b/src/share/classes/java/util/stream/BaseStream.java	Mon Nov 19 17:56:46 2012 -0500
@@ -50,4 +50,10 @@
      * @return {@code true} if this stream may be split for parallel processing.
      */
     boolean isParallel();
+
+    /**
+     * Return the composition of stream flags of the stream source and all intermediate
+     * operations.
+     */
+    int getStreamFlags();
 }
--- a/src/share/classes/java/util/stream/Spliterator.java	Mon Nov 19 13:38:57 2012 -0500
+++ b/src/share/classes/java/util/stream/Spliterator.java	Mon Nov 19 17:56:46 2012 -0500
@@ -93,7 +93,7 @@
 
     /**
      * Returns the count of elements currently retained by this Stream.
-     * This is the count which would be processed by {@code into()} or via
+     * This is the count which would be processed by {@code forEach()} or via
      * {@code iterator()}. If the element count cannot be computed exactly and
      * cheaply then {@code -1} is returned.  The result of calling this method
      * during the traversal phase is unspecified.
--- a/src/share/classes/java/util/stream/StreamOpFlags.java	Mon Nov 19 13:38:57 2012 -0500
+++ b/src/share/classes/java/util/stream/StreamOpFlags.java	Mon Nov 19 17:56:46 2012 -0500
@@ -27,10 +27,34 @@
 import java.util.EnumSet;
 
 /**
- * Flags for known and not known properties of streams and operations.
+ * Flags describing dynamic properties of streams, such as sortedness or distinctness.
  *
- * <p>A stream accessor can logically or flags for the set of properties of the stream.</p>
- * <p>An operation can logically or flags the set of known and and not known properties of the output stream.</p>
+ * Streams either have, or do not have, the specified property.  Intermediate stream
+ * operations can describe whether it preserves, clears, or injects the specified
+ * property.  So, for example, a {@code Stream} derived from a {@code List} has the
+ * "size known" property, and executing the {@code map()} operation on that stream
+ * preserves sized-ness, whereas the {@code filter()} operation clears sized-ness.
+ * Most flags can apply either to a stream or a stream operation, though some can only
+ * apply to stream operations (such as {@code SHORT_CIRCUIT}.)
+ *
+ * Combinations of flags are generally described using int-sized bitmaps for efficiency,
+ * with each flag (generally) taking two bits.  There are two forms of the bitmap:
+ *
+ * <ul>
+ * <li>The stream form, where the specific bits are either set or not set;</li>
+ * <li>The operation form, where two bits are used to encode whether the
+ * operation clears, preserves, or injects the stream property;</li>
+ * </ul>
+ *
+ * For each flag, there is an enum value ({@code SIZED}) as well as derived constants for
+ * set ({@code IS_SIZED}) and cleared ({@code NOT_SIZED}).  To construct the stream form
+ * of flags, OR together the {@code IS} flags ({@code IS_SORTED | IS_DISTINCT}), or to
+ * clear, mask off the {@code IS} flags.  To construct the operation form, you need to
+ * OR together the {@code IS} or {@code NOT} forms.
+ *
+ * To combine the stream bitmap with the operation bitmap for one or more operations, first
+ * combine all the operation bitmaps using {@code combineOpFlags}, and then combine the
+ * stream bitmap with the combined operation bitmap with {@code combineStreamFlags}.
  *
  */
 // @@@ When a new flag is added what should happen for existing operations?
@@ -39,17 +63,11 @@
 public enum StreamOpFlags {
 
     DISTINCT(0),
-
     SORTED(1),
-
     ORDERED(2),
-
     SIZED(3),
-
     SHORT_CIRCUIT(4, false),
-
-    PARALLEL(5)
-    ;
+    PARALLEL(5);
 
     /**
      * The bit pattern for setting a flag.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/StreamFlagsTest.java	Mon Nov 19 17:56:46 2012 -0500
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.tests.java.util.stream;
+
+import org.testng.annotations.Test;
+
+import java.util.*;
+import java.util.stream.Stream;
+import java.util.stream.StreamOpFlags;
+import java.util.stream.Streams;
+
+import static java.util.stream.StreamOpFlags.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * StreamFlagsTest
+ *
+ * @author Brian Goetz
+ */
+@Test
+public class StreamFlagsTest {
+    Stream<String> arrayList = new ArrayList<String>().stream();
+    Stream<String> linkedList = new LinkedList<String>().stream();
+    Stream<String> hashSet = new HashSet<String>().stream();
+    Stream<String> treeSet = new TreeSet<String>().stream();
+    Stream<String> linkedHashSet = new LinkedHashSet<String>().stream();
+    Stream<String> repeat = Streams.repeat(-1, "");
+
+    Stream<?>[] streams = { arrayList, linkedList, hashSet, treeSet, linkedHashSet, repeat };
+
+    private void assertFlags(int value, EnumSet<StreamOpFlags> setFlags, EnumSet<StreamOpFlags> clearFlags) {
+        for (StreamOpFlags flag : setFlags)
+            assertTrue(flag.isKnown(value));
+        for (StreamOpFlags flag : clearFlags)
+            assertTrue(!flag.isKnown(value));
+    }
+
+    public void testBaseStreams() {
+        Stream<String> arrayList = new ArrayList<String>().stream();
+        Stream<String> linkedList = new LinkedList<String>().stream();
+        Stream<String> hashSet = new HashSet<String>().stream();
+        Stream<String> treeSet = new TreeSet<String>().stream();
+        Stream<String> linkedHashSet = new LinkedHashSet<String>().stream();
+        Stream<String> repeat = Streams.repeat(-1, "");
+
+        assertFlags(arrayList.getStreamFlags(),
+                    EnumSet.of(ORDERED, SIZED),
+                    EnumSet.of(DISTINCT, SORTED, SHORT_CIRCUIT, PARALLEL));
+        assertFlags(linkedList.getStreamFlags(),
+                    EnumSet.of(ORDERED, SIZED),
+                    EnumSet.of(DISTINCT, SORTED, SHORT_CIRCUIT, PARALLEL));
+        assertFlags(hashSet.getStreamFlags(),
+                    EnumSet.of(SIZED, DISTINCT),
+                    EnumSet.of(ORDERED, SORTED, SHORT_CIRCUIT, PARALLEL));
+//        assertFlags(treeSet.getStreamFlags(),
+//                    EnumSet.of(SIZED, DISTINCT, SORTED),
+//                    EnumSet.of(ORDERED, SHORT_CIRCUIT, PARALLEL));
+        assertFlags(linkedHashSet.getStreamFlags(),
+                    EnumSet.of(ORDERED, DISTINCT, SIZED),
+                    EnumSet.of(SORTED, SHORT_CIRCUIT, PARALLEL));
+        assertFlags(repeat.getStreamFlags(),
+                    EnumSet.of(ORDERED),
+                    EnumSet.of(SIZED, DISTINCT, SORTED, SHORT_CIRCUIT, PARALLEL));
+    }
+
+//    @@@ Failing due to unexpected extra bits in output
+//    public void testFilter() {
+//        for (Stream<?> s : streams) {
+//            int baseFlags = s.getStreamFlags();
+//            int filteredFlags = s.filter((Object e) -> true).getStreamFlags();
+//            int expectedFlags = SIZED.clearFromFlags(baseFlags);
+//            assertEquals(expectedFlags, baseFlags);
+//        }
+//    }
+}
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/StreamOpFlagsTest.java	Mon Nov 19 13:38:57 2012 -0500
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/StreamOpFlagsTest.java	Mon Nov 19 17:56:46 2012 -0500
@@ -30,12 +30,41 @@
 import java.util.List;
 import java.util.stream.StreamOpFlags;
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
 @Test
 public class StreamOpFlagsTest {
 
+//    @@@ Failing due to unexpected extra bits in output
+//    public void testNullCombine() {
+//        int sourceFlags = StreamOpFlags.IS_SIZED;
+//        int opFlags = StreamOpFlags.INITIAL_OPS_VALUE;
+//        assertEquals(sourceFlags, StreamOpFlags.combineStreamFlags(sourceFlags, opFlags));
+//    }
+//
+//    public void testSameCombine() {
+//        int sourceFlags = StreamOpFlags.IS_SIZED;
+//        int opFlags = StreamOpFlags.INITIAL_OPS_VALUE;
+//        opFlags = StreamOpFlags.combineOpFlags(opFlags, StreamOpFlags.IS_SIZED);
+//        assertEquals(sourceFlags, StreamOpFlags.combineStreamFlags(sourceFlags, opFlags));
+//    }
+//
+//    public void testOpClear() {
+//        int sourceFlags = StreamOpFlags.IS_SIZED;
+//        int opFlags = StreamOpFlags.INITIAL_OPS_VALUE;
+//        opFlags = StreamOpFlags.combineOpFlags(opFlags, StreamOpFlags.NOT_SIZED);
+//        assertEquals(0, StreamOpFlags.combineStreamFlags(sourceFlags, opFlags));
+//    }
+//
+//    public void testOpInject() {
+//        int sourceFlags = 0;
+//        int opFlags = StreamOpFlags.INITIAL_OPS_VALUE;
+//        opFlags = StreamOpFlags.combineOpFlags(opFlags, StreamOpFlags.IS_SIZED);
+//        assertEquals(StreamOpFlags.IS_SIZED, StreamOpFlags.combineStreamFlags(sourceFlags, opFlags));
+//    }
+
     public void test() {
         int sourceFlags = StreamOpFlags.IS_SIZED | StreamOpFlags.IS_DISTINCT;